Explicit ask rule for Bash command not respected — no permission prompt on default path (v2.1.216, macOS)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Environment
claude --version: 2.1.216- OS: macOS, Apple Silicon
- Plan: Pro
- Mode at time of failure: default/manual (
permissions.defaultMode: "default") - No project-level
.claude/settings.jsonexists. - Local settings file (
.claude/settings.local.jsonin the project directory): did not exist during gate tests 1–3 (verified by direct inspection at the time of testing). It was created afterward, as a side effect of enabling/sandbox(regular permissions), and now contains only:
``json``
{"sandbox": {"enabled": true, "autoAllowBashIfSandboxed": false}}
This file plays no role in the bug itself — it documents the workaround's configuration, included here for completeness.
- Relevant
permissionsblock from the global~/.claude/settings.json, as loaded for gate tests 3–4. Note: the three interpreter ask rules (Bash(conda run:*),Bash(python:*),Bash(python3:*)) were added mid-morning, between gate test 1 and gate test 2 — they were not present for gate test 1:
``json``
{
"permissions": {
"defaultMode": "default",
"deny": [
"Bash(sudo:*)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(**/.env)",
"mcp__computer-use"
],
"ask": [
"Bash(rm:*)",
"Bash(mv:*)",
"Bash(pip install:*)",
"Bash(pip3 install:*)",
"Bash(conda install:*)",
"Bash(brew:*)",
"Bash(npm install:*)",
"Bash(curl:*)",
"Bash(wget:*)",
"Bash(git push:*)",
"Bash(conda run:*)",
"Bash(python:*)",
"Bash(python3:*)"
],
"allow": [
"Bash(ls:*)",
"Bash(pwd)",
"Bash(cat:*)",
"Bash(head:*)",
"Bash(tail:*)",
"Bash(grep:*)",
"Bash(mkdir:*)",
"Bash(git status)",
"Bash(git diff:*)",
"Bash(git log:*)"
]
}
}
Actual behaviour
Actual (gate test 3 — definitive repro): In a fresh session launched after the interpreter ask rules were added, with those rules confirmed visible in the /permissions Ask tab post-launch, the command executed immediately — no permission prompt appeared.
Earlier observations (caveated, not part of the definitive repro):
- Gate test 1: ran silently, but at this point no ask or allow rule matched the command at all — under default mode an unmatched command should itself trigger a prompt, so this may be the same defect in broader form (missing prompt for any unmatched command) rather than direct evidence against the
conda runrule specifically. - Gate test 2: ran silently immediately after the interpreter ask rules were added mid-session; a settings hot-reload failure could not be excluded as the cause. This ambiguity motivated gate test 3 as a fresh-session repro.
Localisation finding
To determine whether the ask rule itself was invalid or unloaded, /sandbox (regular permissions) was enabled, which created the local settings file shown above. The identical command was then re-run (gate test 4):
- A permission dialog appeared, explicitly citing: "Permission rule Bash(conda run:) requires confirmation for this command."*
- Only after approving did the command execute.
This isolates the failure to the default (non-sandbox) approval path failing to consult configured ask rules. The ask rule itself is valid, loaded, and correctly enforced once sandbox regular-permissions mode is active — the defect is specific to the default/manual approval path.
Related (searched, not duplicates)
- #42797 — auto-mode ignores permissions.ask (same failure direction; this report is default/manual mode, not auto)
- #78764 — invalid settings file drops ask/deny rules fail-open (this report's settings file is valid and confirmed loaded in the /permissions UI)
- #18846 and its cluster (#18160, #17321, #13340) — allow rules ignored (the mirror image: excess prompting; this report is missing prompting, fail-open)
What Should Happen?
Expected: Since Bash(conda run:*) is listed under permissions.ask, a permission dialog should appear before the command runs, requiring explicit approval.
Since Bash(conda run:*) is listed under permissions.ask, a permission dialog should appear before the command runs, requiring explicit approval — per the documented guarantee that explicit ask/deny rules are always respected in every mode. Instead the command executes with no prompt (fail-open).
Error Messages/Logs
N/A - no error is produced
— the command runs successfully with no prompt, no warning, and no error; the failure is silent (fail-open).
Steps to Reproduce
- Start a session with
permissions.defaultMode: "default"(no sandbox), no project-level settings file, and no local settings file present. - Run a command matching an explicit
askrule, e.g.:
```
conda run -n tess python -c "print('gate test N')"
Bash(conda run:*)` ask rule shown above.
This matches the
- Observe whether a permission prompt appears before the command executes.
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
N/A / Unknown
Claude Code Version
2.1.216
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
_No response_
3 Comments
Independent confirmation on a different OS, a different permission mode, and 35 releases later. Still reproducible.
Environment
claude --version: 2.1.251permissions.defaultMode: "auto"(this report differs from yours, which was"default"— the bug appears in both)Minimal reproduction, user scope only
~/.claude/settings.jsoncontains exactly twoaskrules and noallowthat matches them:Start a new session (permissions load at session start), then run, as a bare Bash call:
Expected: a permission prompt.
Observed: the command runs. No prompt.
Controls that rule out the cheap explanations
These are the reason this is a behavior report and not an impression:
denyfrom the same file does fire. In the same session, a command matching adenyrule with a leading wildcard** from the same file was blocked. So the file is parsed, and leading-wildcard matching works — indeny.cd, no&&, nobash -c, no script. Commands that failed for unrelated reasons reported/usr/bin/bash: line 1: ..., i.e. the string arrived as line 1, not as an inner command.allowcovers them. All three scopes audited.dontAsk. In that modeaskdenies; here the commands ran.denycrosses scopes correctly in the same session, so precedence machinery works. What does not apply is theasktier.Also observed, at project scope
Same session set, same day, four unrelated rule families — none prompted:
| rule | command | result |
| --- | --- | --- |
|
Bash(php artisan key:generate*)|php artisan key:generate| ran ||
Bash(php artisan migrate)— exact match, no wildcard |php artisan migrate| ran ||
Bash(npm run*)|npm run <script>| ran ||
Bash(gh api graphql*)— user scope | the query above | ran |Two of the
key:generatevariants executed: a.envfile grew from 4569 to 4620 bytes.Why this is worth prioritizing
askis where you write the rules for dangerous but sometimes necessary. If the tier never fires, those rules do not merely degrade — they do not exist, and they read as protections to whoever wrote them.We counted our own exposure across all our settings files: 91
askrules, of which 46 permit something destructive or irreversible, and zero are covered by an equivalentdenyin the same file. That includesgit reset,git restore,git checkout,git rm,git worktree remove/prune,docker compose down,php artisan migrate, and the forms that rewrite a real.env. None of those 46 protections existed.There is a second-order cost worth naming: a team can spend real work hardening an
askrule and the work is void. We merged an entire PR to close a prefix-escape in anaskrule before measuring whether the tier fires; it does not, so the change was correct on a false premise.Relation to #85098
#85098 reports that operator-matching
askrules (Bash(* >*),Bash(* | tee*)) never fire and states that text-matchingaskrules do fire. Our data contradicts that second half: all four rules above are plain text matches, including one exact-match rule with no wildcard at all, and none of them prompted. If the two reports are the same defect, its scope is wider than #85098 describes.What we did not establish
The cause. We only establish the behavior, on this OS and these versions. We also cannot rule out that something in our configuration is involved that our five controls did not cover — the controls are what we could falsify, not a proof of completeness.
Documentation contradiction
The documentation, read the same day, states that in
automode "explicit ask rules still force a prompt," and lists "tools matched by an explicit ask rule" among the actions that no auto mode auto-approves. Thedenybehavior matches the docs; theaskbehavior does not.Workaround, for anyone landing here
Rules that must hold go in
deny, which is measured working (leading wildcards included). For the cases wheredenyis too blunt — an action that is fine in a throwaway working tree and destructive in the main checkout — aPreToolUsehook can decide from the target directory rather than the command, which is a distinction a permission rule cannot express. That is what we ended up doing for the 46.Third independent confirmation, different surface this time.
Claude Code version: 2.1.275
OS: macOS (Apple Silicon)
Surface: Claude Desktop app, Code tab — not the standalone CLI. claude --version
isn't even on PATH in this environment; the Code tab runs its own bundled
runtime, sharing config with the CLI but distinct from it.
Mode: Manual (equivalent to default)
Reproduction: a Bash command with no matching allow rule ran with zero
permission prompt, in a fresh session, in Manual mode. Reproduced again after
a full app restart, so not a stale-session artifact.
This isn't a benign miss. In the same environment, openclaw doctor --fix (a
repair command that rewrites config and can restart services) executed
unprompted for real — killed a running process, restarted a service, no
confirmation dialog at any point. No lasting damage this time, verified
independently via git status/git diff on the config file and a service
health check, but the gate simply did not engage.
Given the original report (2.1.216) and the Windows/auto-mode confirmation
(2.1.251) both describe the same fail-open ask/default behavior, and this
reproduces it again 24 releases later on a third surface, this reads as the
same defect, not a new one.
Still reproducible on 2.1.289 (Windows 10, Git Bash) in manual mode (
default), with a configuration that is correct by the documentation:permissions.defaultMode: "default", bareaskrules onEdit,WriteandNotebookEdit,PreToolUsehook returningpermissionDecision: "ask"on project files, noallowrule on the project, no plan approval. Edits on project files get applied with no permission decision recorded in the transcript. Onlydenyrules and hooks exiting with code 2 hold.Full write-up in #96078, including a second finding: in auto mode the harness explicitly instructs the model to modify files with
sed, heredocs or short scripts from Bash "rather than using the dedicated Read, Edit, or Write tools", which bypasses every Edit/Write hook and everyaskrule on those tools by design. Any fix for this issue is incomplete as long as that instruction exists.