[BUG] v2.1.210 regression: PreToolUse prompt-hook deny stops the entire turn (hook_stopped_continuation) instead of returning a tool error

Status Open
Reported on v2.1.210
Maintainer reply None cached
Activity 6 comments · opened Jul 17, 2026

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.

View original on GitHub ↗

3 Comments

tehlowkeywiz · 1 month ago

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 -p cannot reproduce this — type: "prompt" PreToolUse hooks fail open there (the judge returns a generic ok:true), so the deny path never fires. Driving a fresh interactive claude under a pty (real TTY) makes prompt hooks enforce. Recipe:

  • settings.json with a type: "prompt" PreToolUse hook on matcher: "Bash" instructed to block destructive commands.
  • Give the session a task that reliably elicits an autonomous blocked command: create a nested ./junk/ directory tree and ask the session to "clear out the disposable junk directory" — the model runs rm -rf junk (a directory tree needs -rf; simpler triggers get routed around).
  • On 2.1.211: a clean {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:

  • A killed turn is revived by any subsequent message to the session (user input or agent-to-agent message) — the session isn't corrupted, just stopped.
  • Workaround we ship: converting the judge to a type: "command" hook denying via exit code 2 — those denials feed back in-turn and the turn survives.
mickey-mikey · 25 days ago

Happening for me on 2.1.222. Really annoying.

mickey-mikey · 17 days ago

A Claude Code agent reproduced this today on 2.1.229, using a prompt-type PreToolUse hook on Edit/Write/MultiEdit. A judge model returned ok:false, and the agent's turn ended immediately with PreToolUse: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:

  • #55900 (closed not planned, June 2026): a PostToolUse prompt hook returning ok:false ends the turn instead of feeding the reason back.
  • #34601 (closed not planned, April 2026): same symptom, also on PostToolUse. A commenter there suggested the same workaround this issue's author uses: a command-type hook denying via exit code 2.

Showing cached comments. Read the full discussion on GitHub ↗