PreToolUse "ask" permissionDecision (and permissions.ask list) not enforced — tools run without confirmation

Status Open
Reported on v2.1.215
Maintainer reply None cached
Activity 3 comments · opened Jul 20, 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?

Summary

PreToolUse hook permissionDecision: "ask" (and baseline permissions.ask list entries) are not enforced — tools run immediately with no confirmation prompt, even when a hook explicitly returns an "ask" decision with a reason.

Environment

  • Claude Code version: 2.1.215
  • OS: Windows 11 Pro (10.0.26200)
  • Shell tool used: PowerShell (Windows PowerShell 5.1 / powershell.exe)
  • Permission mode: default (confirmed via Shift+Tab indicator — not bypass/auto-accept)
  • Config: ~/.claude/settings.json, permissions.ask includes PowerShell, Edit, Write, NotebookEdit; a PreToolUse hook is registered on the PowerShell matcher

Steps to reproduce

  1. Configure a PreToolUse hook on the PowerShell matcher that reads the tool-call JSON from stdin and, for commands matching a "dangerous" pattern (e.g. Remove-Item), returns:

``json
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"..."}}
``

  1. Verify the hook script works correctly by piping a simulated tool-call payload into it directly (outside the harness) — confirm it returns the ask JSON above for a Remove-Item command.
  2. Through the actual Claude Code session, ask Claude to run a Remove-Item command via the PowerShell tool.

What Should Happen?

Expected behavior

The tool call should pause and prompt for explicit user confirmation before running, per the hook's permissionDecision: "ask" output (and/or because PowerShell is on the permissions.ask list).

Error Messages/Logs

## Actual behavior

The command executes immediately. No confirmation prompt is shown. This was verified by checking that the target file was actually deleted, with no pause in the conversation.

This is not limited to the hook — `Write` and `NotebookEdit` (also on the `permissions.ask` list, no hook attached) exhibited the same behavior: zero prompts for an entire session, for any tool on the `ask` list.

Steps to Reproduce

Investigation / ruled-out causes

  • Hook content was not the issue. Initially the hook script itself was silently crashing (see below), but even after fixing that and confirming — via direct, out-of-harness invocation — that it returns the correct ask JSON for dangerous commands and no output (implicit allow) for safe ones, live tool calls through the harness still bypass confirmation entirely.
  • Not a permission-mode issue. Confirmed session was in default mode via the Shift+Tab indicator, not bypassPermissions or acceptEdits.
  • Not a settings.local.json override. No local override file present.
  • Separately found and fixed a real hook-invocation bug that may be related or may be a distinct issue: "type": "command" hooks are executed via /usr/bin/bash -c "<command>" regardless of host OS. On Windows, a hook command written as a PowerShell script (as shown in Claude Code's own Windows hook examples) is therefore fed to bash, which cannot parse PowerShell syntax, producing an immediate syntax error on every invocation. Worked around this by moving the hook logic into a standalone .ps1 file and setting command to powershell.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File "<path to .ps1>". This fixed the hook crashing, but did not fix the underlying lack of enforcement described above — suggesting the enforcement gap is a separate, harness-level issue from the shell-invocation one.

Claude Model

Sonnet (default)

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.215 (Claude Code)

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

Additional context

Given the shell-invocation bug above, it's plausible other Windows users configuring PowerShell-based safety hooks (e.g. following any PowerShell-syntax hook example) are silently getting non-functional hooks without realizing it, since the failure mode observed was: hook crashes silently → tool call proceeds anyway → no visible error to the user in some cases, a bash syntax error surfaced as a hook error in others (both observed across two sessions six days apart, same config, same commands).

View original on GitHub ↗

3 Comments

Lightnomad76 · 28 days ago

Still present in 2.1.220, and it is not mode-specific.

This issue reports permissionMode: default. I see identical behavior with
"defaultMode": "auto" in ~/.claude/settings.json, in a fully interactive
session (not background, not non-interactive, not bypassPermissions — those
conditions are covered by #79501 / #80138 / #77212, this is none of them).

Isolation, same session / same hook / same JSON shape, one word changed:

permissionDecision: "deny" -> command blocked, reason surfaced. Correct.
permissionDecision: "ask" -> command runs, no prompt at all.

So the ask path is what's broken, not the rule syntax and not the hook
plumbing — deny travels the same route and is honored.

Also confirming the settings.json half on this build: 17 explicit ask rules,
all listed in /permissions under "will always ask for confirmation", none of
them ever prompts. Failure is silent — no error, no warning, no log entry.

Env: Claude Code 2.1.220, Windows 11 Pro 10.0.26200, PowerShell.
Filed via /feedback as 37b8c9d8-7ce8-450b-baac-ff5f7d65c0df.

KazukiShinomiya · 28 days ago

Reproduced on Linux, so this is not Windows- or PowerShell-specific — the platform:windows label looks too narrow.

Environment

  • Claude Code 2.1.220
  • WSL2, Ubuntu 24.04, Linux 5.15 (x86_64)
  • Bash tool (not PowerShell), /usr/bin/zsh as the shell
  • Interactive session; permissionMode default and acceptEdits — same result in both
  • No managed settings

Controls

Both directions of the control were run with no change other than the decision string, which rules out the hook mechanism, the JSON shape, exit codes, and settings loading:

  1. Hook path. A PreToolUse hook on the Bash matcher returning

``json
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"ask","permissionDecisionReason":"test"}}
``

produces no prompt; the command runs. Changing "ask" to "deny" in the same script blocks the call immediately and renders permissionDecisionReason.

  1. Rule path, no hooks involved. With "ask": ["Bash(rm:*)"] and "deny": ["Bash(touch:*)"] in the same ~/.claude/settings.json: touch /tmp/x is blocked, rm -f /tmp/x runs with no prompt. Both rules load together, so "settings were not picked up" cannot explain the difference.

I confirmed the hook actually executes (rather than being skipped) by having the script append to a log file — the log grows on every matching call in both the ask and deny variants.

Why this one is worth prioritising

The failure is silent and looks healthy from every angle a user can check: /hooks lists the hook as registered, the hook process runs, and the judge emits a correct decision — but nothing reaches the user. A guardrail built on ask therefore enforces nothing while appearing installed. That is strictly worse than having no guardrail, since there is no signal to discover it from.

The documentation still recommends ask as the middle ground between allow and deny (Configure permissions → Extend permissions with hooks: "The hook output can deny the tool call, force a prompt, or skip the prompt"). On this version that recommendation yields no protection on Linux either.

Workaround for anyone landing here: use deny, or enforce outside Claude Code (git hooks, filesystem permissions).

JHK · 12 days ago

Hit both halves of this on Linux with the Bash tool on 2.1.234, in an interactive session started as claude --permission-mode auto: a permissions.ask entry did not prompt, and a PreToolUse hook returning permissionDecision: "ask" for the same command did not prompt either. deny still gates in the same session.

Full write-up with the repro and the controls: #87639. The parts that may be useful here:

  • The hook was provably live and provably emitted ask at the moment it was ignored. A date >> sentinel prefixed onto the hook command wrote its file, and piping the command through the hook script as stored in settings.json printed permissionDecision: "ask".
  • The same rules gate correctly under claude -p --permission-mode auto on the same build, including a check where a user-scope ask rule beat a matching user-scope allow rule. So rule syntax, user-scope loading, and ask-over-allow precedence all behave; the failure showed up only in the interactive session.

Also agreeing with the earlier comments that platform:windows is too narrow: Windows/PowerShell, WSL2/Bash, and now Fedora/Bash.