[BUG] v2.1.210 regression: PreToolUse prompt-hook deny stops the entire turn (hook_stopped_continuation) instead of returning a tool error
Environment: Claude Code 2.1.210 / 2.1.211, macOS (darwin 25), interactive sessions.
Setup: a type: "prompt" PreToolUse hook on Bash (LLM security judge using the documented {ok, reason} contract from https://code.claude.com/docs/en/hooks).
Before (≤2.1.209): judge ok:false denials fed back to the model as is_error tool_results; the model reacted in-turn. Our session transcripts show 74 such denials from 2.1.179 → 2.1.209 with 100% turn survival (the latest just hours before upgrading to 2.1.210).
After (2.1.210+): the same denial emits attachment.type: "hook_stopped_continuation" and ends the turn:
- no feedback reaches the model;
- the Stop-hook chain is skipped (no
stop_hook_summary; Stop-hook notification sounds/commands don't fire), so the stall is silent; - unattended sessions and subagents hang until external input arrives (subagents have no user-input rescue path at all — only an incoming teammate message revives them).
7/7 such events in our transcripts occur on 2.1.210/211; zero on any version ≤2.1.209 (~1,800 transcript files scanned for the exact marker).
Expected: per the hooks docs, prompt-hook ok:false "maps to permissionDecision: "deny" … blocks the action" — the action, not the turn. Command hooks (exit 2 and permissionDecision: "deny") still behave correctly on 2.1.211 — deny feeds back, turn continues.
Suspected change: 2.1.210's "Fixed a hook callback timeout being misreported to the model as a user rejection…" touching the hook-result path; possibly re-engaging the v2.1.92 "restored preventContinuation:true semantics for non-Stop prompt-type hooks" behavior.
Repro: interactive session; PreToolUse type: "prompt" hook on Bash instructed to deny destructive patterns; run a command the judge blocks (e.g. a recursive delete of a nonexistent path). The turn ends with "PreToolUse:Bash hook stopped continuation". Instructing the judge model to emit "preventContinuation": false in its JSON changes nothing — the stop appears to be harness-asserted, independent of the judge's response.
Secondary observation (possibly its own issue): in headless -p mode, prompt hooks appear to fail open — the judge model returns generic ok:true even when the hook prompt explicitly instructs an unconditional ok:false, so prompt-hook enforcement seems absent in headless runs.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Update: still reproduces on 2.1.214 (2.1.215 not yet tested; no CHANGELOG entry through 2.1.215 mentions this deny path).
Two new data points since filing:
1. On-demand reproduction (pty harness). Headless
-pcannot reproduce this —type: "prompt"PreToolUse hooks fail open there (the judge returns a genericok:true), so the deny path never fires. Driving a fresh interactiveclaudeunder a pty (real TTY) makes prompt hooks enforce. Recipe:settings.jsonwith atype: "prompt"PreToolUse hook onmatcher: "Bash"instructed to block destructive commands../junk/directory tree and ask the session to "clear out the disposable junk directory" — the model runsrm -rf junk(a directory tree needs-rf; simpler triggers get routed around).{ok:false, reason}deny kills the entire turn (stopped continuation) instead of feeding back as a tool error. On ≤2.1.209 the same denials fed back in-turn (74/74 turn survivals across 2.1.179→2.1.209 in our probes; 8/8 turn-deaths on 2.1.210/2.1.211).2. Confirmed live on 2.1.214. In our pty runs on 2.1.214 the judge model happened to allow
rm -rf(verdict nondeterminism), so the clean-deny path wasn't isolated there via pty. But the mechanism fired in a real 2.1.214 working session: an armed prompt hook returned a non-continue result on a Bash call →PreToolUse:Bash hook stopped continuation→ the turn died mid-task. Same failure family.Two adjacent observations that may help triage:
type: "command"hook denying via exit code 2 — those denials feed back in-turn and the turn survives.Happening for me on 2.1.222. Really annoying.
A Claude Code agent reproduced this today on
2.1.229, using aprompt-typePreToolUsehook onEdit/Write/MultiEdit. A judge model returnedok:false, and the agent's turn ended immediately withPreToolUse:Write hook stopped continuation. The agent couldn't react to it in the same turn.In this run, the judge's reasoning landed in the agent's context as a tool-result error before the turn ended, but the agent never got to act on it.
Two earlier reports show the same symptom, both closed without a fix:
PostToolUseprompt hook returningok:falseends the turn instead of feeding the reason back.PostToolUse. A commenter there suggested the same workaround this issue's author uses: acommand-type hook denying via exit code 2.