[BUG] Claude Repeatedly asks for permission to run commands, even when I've already granted it

Status Fixed / completed
Maintainer reply ✓ Yes — bcherny
Activity 14 comments · opened Apr 18, 2025 · closed Apr 7, 2026
💡 Likely answer: A maintainer (bcherny, collaborator) responded on this thread — see the highlighted reply below.

Environment

  • Platform (select one):
  • [X] Anthropic API
  • [ ] AWS Bedrock
  • [ ] Google Vertex AI
  • [ ] Other: <!-- specify -->
  • Claude CLI version: 0.2.73 (Claude Code)
  • Operating System: MacOS 15.4.1
  • Terminal: iTerm2/tmux

Bug Description

In my project directory, I have a script (bin/rspec) to run tests. For some reason, even though I always choose Yes, and don't ask again for rspec commands in <path>, it keeps asking for permission. One strange thing is that Claude Code omits the bin/ in the message. I wonder if that's related?

Steps to Reproduce

  1. Ask Claude to run tests with bin/rspec <path>
  2. Answer Yes, and don't ask again for rspec commands in <path>
  3. Ask Claude to run tests again
  4. Claude prompts again on whether it can run the command

Expected Behavior

After step 3 I should not be prompted again

Actual Behavior

After step 3 I am prompted again

View original on GitHub ↗

14 Comments

leonaburime-ucla · 1 year ago

So damn frustrating. I want to leave it running while I do something else and it just freezes asking for permission when I just granted it. Awful!

ZivBA · 12 months ago

Not sure in which of the many identical issues to put this, so I'm hoping someone catches it here:
I believe the issue stems from inconsistent expansion of the ~ sign (home folder).
Sometimes Claude will try to read from the relative path (~/someFolder), and when you approve the action, the actual permission written to the settings will be "Read(/home/<username>/someFolder/**)"
But when Claude tries again to read from ~/someFolder, it will not match the rule in the settings JSON.
Manually adding the relative path (as ~/someFolder) to permissions (from Claude /permissions or directly to settings json) will resolve the issue.

I believe a proper solution would be for claude to identify home-folder paths, and add both the relative and absolute paths to the 'allowed' list, so both versions will work (or for Claude to always expand homefolder paths behind the scenes?)

cruftyoldsysadmin · 9 months ago

@claude Do better. Take 2 days and stop building new broken features, just fix the old broken features.

adriand · 9 months ago

This happens with me all the time, and it seems to be with just certain commands. Right now the problematic command is jq. Local settings for Claude:

{
  "permissions": {
    "allow": [
      "Bash(bundle exec rake:*)",
      "Bash(find:*)",
      "Bash(bundle exec rails runner:*)",
      "WebFetch(domain:github.com)",
      "Bash(xargs sed:*)",
      "Bash(npm run typecheck:*)",
      "Bash(bundle exec rspec:*)",
      "Bash(git log:*)",
      "Bash(npm run:*)",
      "Bash(npx eslint:*)",
      "Bash(bundle info:*)",
      "Bash(bundle exec rails console:*)",
      "Bash(git diff:*)",
      "Bash(rails generate model:*)",
      "Bash(rails generate migration:*)",
      "Bash(rake:*)",
      "Bash(bundle install:*)",
      "Bash(bundle exec rails generate:*)",
      "Read(//tmp/**)",
      "Bash(bin/rails generate:*)",
      "Bash(bin/rails db:migrate:*)",
      "Bash(psql:*)",
      "Bash(tee:*)",
      "Bash(test:*)",
      "Bash(bin/rails runner:*)",
      "Bash(cat:*)",
      "Bash(jq:*)",
      "WebFetch(domain:kamal-deploy.org)"
    ],
    "deny": [],
    "ask": []
  }
}

Despite that:

 Bash command

   jq '.elements | length' /PATH_REDACTED
   Count total elements in scaffold

 Do you want to proceed?
 ❯ 1. Yes
   2. Type here to tell Claude what to do differently

It's quite irritating. I also notice that it doesn't give me the option to allow these requests going forward. It's like it "knows" I've already told it that it can use the tool, but feels the need to ask me each time anyway.

github-actions[bot] · 8 months ago

This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.

exFalso · 7 months ago

Fellow frustrated user here. From what I can tell there are actually two issues here

  1. Claude Code asking for a permission even after it's been given (@f1sherman)
  2. Claude Code not even offering "Yes, and don't ask again" (@adriand)

I'm experiencing 2, for which I found a strange workaround: Create a runnable script that wraps the call, and instruct claude to prefix the commands with this script. It will still re-ask a couple of times, but to me it seems to work better.

undenuicap · 6 months ago

this really sucks. I think if you grant permission to 'cd' and 'grep' then 'cd arg1 && grep arg2' is NOT allowed which is dumb. They should treat the string split into cmd and check if both are allowed.

xyb-es · 6 months ago

still there - for me its everything with mvn - utterly frustrating.
tried adding "Bash(mvn:*)", to local project settings, global settings, nope, still asks to run every single bloody test :S

<img width="193" height="193" alt="Image" src="https://github.com/user-attachments/assets/82412031-597a-4dd0-bf65-bb979f530168" />

paulsturgess · 5 months ago

I found if I have the rule "Bash(./bin/rails db:*)" then Claude would keep asking me if it could run the command Bash(./bin/rails db:migrate 2>&1).

Then when I updated the settings to remove the : and asked it again. It happily ran the command.

So perhaps it can't handle the : in the setting glob pattern. 🤷

Update reading the permission docs it says:

The space before * matters: Bash(ls *) matches ls -la but not lsof, while Bash(ls*) matches both. The legacy :* suffix syntax is equivalent to * but is deprecated.
yurukusa · 5 months ago

The settings.json allow-list has known issues with pattern matching (colons, pipes, chained commands, ~ expansion, etc.). A more reliable approach is to use a PreToolUse hook that pattern-matches and auto-approves commands before the permission prompt fires.
Add to .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/auto-approve.sh"
          }
        ]
      }
    ]
  }
}

Create ~/.claude/hooks/auto-approve.sh:

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
[ -z "$COMMAND" ] && exit 0
SAFE_PATTERNS=(
  '^(ls|pwd|echo|cat|head|tail|wc|find|which|whoami|date|uname)(\s|$)'
  '^(git (status|log|diff|branch|show|stash list|remote -v))(\s|$)'
  '^(npm (test|run|install|ci|ls)|npx |yarn |pnpm )'
  '^(bundle exec (rake|rspec|rails)|bin/(rspec|rails|rake))'
  '^(mvn (test|compile|package|verify|clean))'
  '^(python -m pytest|python -m py_compile|pip install)'
  '^(cargo (test|build|check|clippy|run)|rustc )'
  '^(go (test|build|run|vet|fmt))'
  '^(make(\s|$)|cmake )'
  '^(grep|rg|ag|ack|sed |awk )'
  '^(mkdir|touch|cp |mv )'
  '^(jq |curl -s|wget -q)'
  '^cd\s'
)
for pattern in "${SAFE_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qE "$pattern"; then
    exit 0
  fi
done
DENY_PATTERNS=(
  'rm -rf\s+/'          # rm -rf with absolute path
  'git push.*--force'
  'git reset --hard'
)
for pattern in "${DENY_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qE "$pattern"; then
    echo "BLOCKED by hook: $COMMAND"
    exit 2
  fi
done
exit 0

Why this works better than permissions.allow:

  1. Chained commands: cd foo && grep bar triggers re-prompting because the allow-list matches the full string. The hook matches the first command in the chain
  2. Colons: bin/rails db:migrate breaks allow-list matching (see @paulsturgess's comment). Regex has no such limitation
  3. Full regex power: You can match mvn test.* instead of trying to enumerate every subcommand variant
  4. Deny list: You can also block dangerous patterns, which permissions.allow can't do

Tip for chained commands: If you want to approve cd project && mvn test, you can add a pattern like '^cd\s.*&&\s*(mvn|npm|cargo)' to catch the common chain patterns.
Hooks run before each tool call, so this covers every Bash invocation throughout the session — no more repeated permission prompts for the same tool pattern.

bcherny collaborator · 4 months ago

The original bug from this report — the permission prompt labeling the rule as rspec (dropping bin/), so the saved rule could never match bin/rspec ... — is fixed. The model-based prefix extractor that stripped the path is no longer used; the local extractors keep the full path, so today the saved rule is Bash(bin/rspec:*) (or the exact command), which matches on the next run.

This thread has accumulated several unrelated follow-ons that have their own trackers (e.g. ~ expansion mismatch, cd && grep compound commands, db:* not matching db:migrate — that last one is working as intended; the legacy :* suffix requires a space boundary). See #29967, #29529, #31889, #31735, #38261 for those.

We'll close this one for the original bin/rspec report. Please open a fresh issue if you hit a specific case on a current version.

f1sherman · 4 months ago

Makes sense to me, thanks!

github-actions[bot] · 4 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.