Fix Slack notify prompt to use literal values, not $VAR#999
Merged
Conversation
Claude Code's permission matcher denies any Bash command containing an unexpanded shell variable (e.g. "$PR_NUMBER"), regardless of whether the rest of the command matches an allowedTools wildcard like Bash(gh:*). The prompt told Claude to build its gh command from env vars using shell expansion, so every attempt was denied no matter how it was quoted, and Claude burned all 30 turns retrying variations before timing out. Interpolate the PR number, repo, URL, and workspace path directly into the prompt text via GitHub Actions expressions instead, so the Bash command Claude runs contains literal values with no expansion. None of these are PR-content-controlled (they come from trusted event/repository context), so embedding them is safe. Verified locally: ran the rendered prompt through the claude CLI with the same allowedTools/model/max-turns and got num_turns=3, permission_denials=0, and a correct output file.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Root cause found and confirmed locally, not another guess this time.
Claude Code's permission matcher denies any Bash command containing an unexpanded shell variable (
$PR_NUMBER,${GH_REPO}, etc.), regardless of whether the rest of the command matches anallowedToolswildcard likeBash(gh:*). It can't statically verify that expanding an unknown variable won't smuggle in extra shell metacharacters, so it always falls back to "needs approval", which is impossible to satisfy with no human in the loop. The prompt told Claude to build itsgh pr viewcommand from env vars using exactly that shell-expansion syntax, so every attempt was denied no matter how it was quoted or wrapped, and Claude burned through all 30 turns trying workarounds (brace expansion, subshells,printenv, Pythonsubprocess, writing a helper script) before timing out.I reproduced this directly: ran the
claudeCLI locally with the literal prompt text and the same--allowedTools "Bash(gh:*),Write" --setting-sources user --max-turns 30, and gotpermission_denials: 19anderror_max_turns, denial #1 being the exact literal command from the prompt. The denial reason was"Contains simple_expansion".Fix: interpolate the PR number, repo, URL, and workspace path directly into the prompt text via GitHub Actions expressions (
${{ github.event.pull_request.number }}, etc.) instead of telling Claude to reference env vars in the Bash command. None of these four values are PR-content-controlled (they're trusted event/repository/runner context, not PR title/body), so embedding them directly carries no injection risk, the existing design already correctly avoids embedding actual PR content this way.Verified the fix the same way: rendered the new prompt with PR #992's real values and ran it through the local
claudeCLI with identical flags. Result:num_turns: 3,permission_denials: 0, and a correctly-formed output JSON with accurate PR data and reviewer notes.Type of change
Related issues/PRs
Follow-up to #994, #995, #996, #997, #998
Submitter checklist
Content and formatting
Reviewer checklist
Content