[BUG] "Always Allow" writes a rule that cannot satisfy the gate that blocked the command, so an identical command re-prompts forever (shell redirect outside cwd)

Status Open
Reported on v2.1.241
Maintainer reply None cached
Activity 0 comments · opened Aug 23, 2026

Preflight

  • [x] Searched existing issues. Closest are #83406 (suggested patterns too narrow) and #81639 (rules written malformed on Windows). This is neither: the rule written here is well-formed and genuinely general, it is simply irrelevant to the check that actually blocked the command.

Summary

For a Bash command whose only privileged part is a shell redirect to a path outside the session's working directory, the permission prompt's "Always Allow" writes a persistent Bash(...)/Read(...) rule. That rule does not — and cannot — satisfy the filesystem write gate that blocked the command. The next byte-identical invocation prompts again, in the same session, one tool call later, and re-proposes the rule it already holds. No number of "Always Allow" clicks ever resolves it.

Environment

  • Claude Code 2.1.241 (the direct repro below)
  • Also reproduced over ACP via @agentclientprotocol/claude-agent-acp@0.66.0, cwd=/home/sprite, writing to /tmp — the adapter only relays optionId, and the loop is identical, so this is not adapter-specific.

Repro A — the two gates are separate (plain CLI, no ACP)

mkdir -p ~/permtest/.claude && cd ~/permtest
echo '{"permissions": {"allow": ["Bash(echo hello *)", "Read(//tmp/**)", "Bash(cat /tmp/*)"]}}' \
  > .claude/settings.local.json

# control — operator-free, matches the rule, runs silently
claude -p 'Run this exact command with Bash: echo hello world' --permission-mode default </dev/null

# also fine — chained, both halves match the rule
claude -p 'Run this exact command with Bash: echo hello a && echo hello b' --permission-mode default </dev/null

# the case — redirect outside cwd, every relevant rule already granted
claude -p 'Run this exact command with Bash: echo hello > /tmp/permfoo && cat /tmp/permfoo' --permission-mode default </dev/null

The third prints:

Output redirection to '/tmp/permfoo' was blocked. For security, Claude Code may only
write to files in the allowed working directories for this session: '/Users/jake/permtest'.

So the redirect is gated by a filesystem check that no Bash(...) or Read(...) rule participates in. Bash(echo hello *) is not too narrow — it matches, and it is not what is consulted.

Repro B — therefore "Always Allow" never terminates

Interactively (measured over ACP, cwd=/home/sprite), one prompt: run echo hello > /tmp/foo && cat /tmp/foo twice, one after the other.

  1. First invocation prompts. The allow_always option advertises:
{"description": "Allow Bash calls matching echo hello *, Read calls matching //tmp/**",
 "lifetime": {"scope": "persistent", "storage": "project_local"}}
  1. Answer Always Allow. It runs.
  2. The second, byte-identical invocation — same session, same process, seconds later — prompts again, and proposes the same rule.

Across three separate turns ("write 'hello' to /tmp/foo", "write 'bar' to /tmp/foo", "write 'hello' to /tmp/foo", Always Allow each time) the settings file accumulates:

{"permissions": {"allow": ["Bash(echo hello *)", "Read(//tmp/**)", "Bash(echo bar *)"]}}

Turn 3 ran a command byte-identical to turn 1, whose rule was already on disk, and prompted anyway.

Expected

Choosing "Always Allow" for a command suppresses the prompt for that same command afterwards — or, if the blocking gate cannot be granted by a permission rule, the option is not offered as "always" in the first place.

Actual

A persistent rule is written, is never consulted for the gate that blocked, and the identical command prompts indefinitely.

Why it matters beyond the annoyance

Users read the repeated prompt as the button being broken and start reaching for --dangerously-skip-permissions. An "always" that cannot terminate trains people out of the permission system entirely.

Suggested directions

Either would fix it:

  • have "Always Allow" propose a change that covers every gate the command tripped, including the working-directory filesystem grant (persistently, not only for the session); or
  • when the blocking gate has no rule representation, say so on the prompt and drop the "Always Allow" option rather than offering a grant that provably cannot take effect.

The second is strictly more honest and probably cheaper.

View original on GitHub ↗