Explicit ask rule for Bash command not respected — no permission prompt on default path (v2.1.216, macOS)

Status Open
Reported on v2.1.216
Maintainer reply None cached
Activity 3 comments · opened Jul 21, 2026

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.json exists.
  • Local settings file (.claude/settings.local.json in 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 permissions block 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 run rule 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

  1. Start a session with permissions.defaultMode: "default" (no sandbox), no project-level settings file, and no local settings file present.
  2. Run a command matching an explicit ask rule, e.g.:

``
conda run -n tess python -c "print('gate test N')"
`
This matches the
Bash(conda run:*)` ask rule shown above.

  1. 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_

View original on GitHub ↗

3 Comments

joseantonio-Flora · 1 month ago

Independent confirmation on a different OS, a different permission mode, and 35 releases later. Still reproducible.

Environment

  • claude --version: 2.1.251
  • OS: Windows 11 (10.0.26200)
  • Mode at time of failure: permissions.defaultMode: "auto" (this report differs from yours, which was "default" — the bug appears in both)
  • Tool used: the Bash tool (Git Bash), each command as its own call with the bare command string

Minimal reproduction, user scope only

~/.claude/settings.json contains exactly two ask rules and no allow that matches them:

{
  "permissions": {
    "defaultMode": "auto",
    "ask": ["Bash(gh api graphql*)", "PowerShell(gh api graphql*)"]
  }
}

Start a new session (permissions load at session start), then run, as a bare Bash call:

gh api graphql -f query='{viewer{login}}'

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:

  1. **The settings file is loaded, and deny from the same file does fire. In the same session, a command matching a deny rule with a leading wildcard** from the same file was blocked. So the file is parsed, and leading-wildcard matching works — in deny.
  2. Not the tool route. Every command was sent through the Bash tool as its own call with the bare string: no cd, no &&, no bash -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.
  3. No allow covers them. All three scopes audited.
  4. Not dontAsk. In that mode ask denies; here the commands ran.
  5. deny crosses scopes correctly in the same session, so precedence machinery works. What does not apply is the ask tier.

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:generate variants executed: a .env file grew from 4569 to 4620 bytes.

Why this is worth prioritizing

ask is 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 ask rules, of which 46 permit something destructive or irreversible, and zero are covered by an equivalent deny in the same file. That includes git 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 ask rule and the work is void. We merged an entire PR to close a prefix-escape in an ask rule 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 ask rules (Bash(* >*), Bash(* | tee*)) never fire and states that text-matching ask rules 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 auto mode "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. The deny behavior matches the docs; the ask behavior does not.

Workaround, for anyone landing here

Rules that must hold go in deny, which is measured working (leading wildcards included). For the cases where deny is too blunt — an action that is fine in a throwaway working tree and destructive in the main checkout — a PreToolUse hook 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.

Alf-Dev-J · 24 days ago

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.

gmantova · 10 days ago

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", bare ask rules on Edit, Write and NotebookEdit, PreToolUse hook returning permissionDecision: "ask" on project files, no allow rule on the project, no plan approval. Edits on project files get applied with no permission decision recorded in the transcript. Only deny rules 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 every ask rule on those tools by design. Any fix for this issue is incomplete as long as that instruction exists.