Claude Code 2.1.295 hooks can fail closed when they break

Until this release, a Claude Code guard hook that crashed, hung or returned an odd exit code simply stepped aside, and the action it was supposed to check went ahead. Version 2.1.295, according to the release notes on Github, adds onFailure: "block" for command and HTTP hooks, so those failures now stop the action.
At a glance
- If you rely on hooks to stop risky actions, you can now make a broken hook count as a refusal. Before this setting, a broken hook amounted to silent permission.
- The same release adds OSC 7501 terminal status, a gateway setting timeouts.upstream_ttfb_ms that caps how long Bedrock, Vertex, Foundry and other cloud streams may take to start, and per-upstream model lists.
- The changelog does not say which hook events honour the new setting, whether managed settings can enforce it, or whether other hook types will get an equivalent switch.
If you have not followed hooks closely: according to Paul-schick, they arrived in June 2025 as handlers that run deterministically at fixed points in a tool call. CLAUDE.md instructions are different, because the model may occasionally skip them. The Munder Difflin Blog counts 33 hook events documented in 2.1.284. It also notes earlier limits on hooks themselves, such as 2.1.143 capping runaway Stop hooks at eight blocks in a row.
onFailure: "block" turns three kinds of hook failure into a hard stop
The new setting applies to command and HTTP hooks. Mark one with onFailure: "block", and three situations that used to let the action through now block it: the hook cannot start, it times out, or it exits with a code Claude Code does not expect.
Several fixes in the same release close quieter gaps in guards. A mod's hook could be handed a deeply nested tool input that was cut short with no error, so a guard could pass content it never saw. A mod reloading during a plugin hooks worker restart could get its calls past another mod's guard hook if that hook had a .catch. Those calls are now refused.
Smaller hook fixes round this out. An async hook's JSON output printed over several lines is no longer ignored. When a mod denies a tool call after the tool has already run, Claude and you are now told that the tool ran and a plugin withheld its result.
Exit code 2 already blocked a tool call, as long as the hook answered
A guard hook already had a way to say no. Exit code 2 blocks the action and feeds the error back to Claude. A command hook receives its input as JSON on stdin, and a JSON reply can also block or redirect. PreToolUse fires before a tool runs and can stop it. PostToolUse fires after the tool succeeds and cannot, because the tool has already run.
Claude Code Docs add that HTTP hooks receive the same input as a POST request body. Think of a bouncer at a rope: the rope only closes if the bouncer is there to close it. Before this release, an absent bouncer meant an open rope. With onFailure: "block", an absent bouncer counts as a no.
Third-party guards show what the old default looks like in practice. On GitHub, the agent-run-guard project is described as failing open on internal errors. Guard hooks listed there are used to block .env reads, destructive git and filesystem commands, and PII exfiltration.
The Claude apps gateway can now cap how long a cloud stream takes to start
timeouts.upstream_ttfb_ms now works on the gateway's Bedrock, Vertex, Foundry and other cloud upstreams. The value limits how long a stream may take to start there. Past that limit, the request fails over or gets a 502. Each upstream also takes an optional models list: only the listed models are sent there, failover included, and one * in an entry acts as a wildcard.
Debugging gets easier too. The inference audit event gains upstream_request_id, which is the ID from Amazon Bedrock, the Anthropic API or another upstream. Successful responses also carry a request-id header, so Claude Code telemetry matches the gateway's audit log. A read-only PostgreSQL database now triggers a warning every 30 seconds that says what fails and how to recover.
Two changes affect cost and errors. Background requests behind the gateway use Haiku 4.5 instead of the session's model wherever the gateway serves Haiku 4.5. On Bedrock, token counts come from AWS's CountTokens API once you grant bedrock:CountTokens. Sessions on a [1m] model no longer fail on every request when an upstream refuses the context-1m beta, because Claude Code resends without it.
OSC 7501 lets a terminal show whether Claude Code is working, waiting or done
Terminals that implement the Program Status Protocol can now display Claude Code's state: working, waiting on you, or done. A related change helps scripts. When a claude -p run stays open after its last turn, it prints a line on stderr saying what it is waiting for, as long as stderr is a terminal.
Several MCP limits change. Remote servers in headless and SDK sessions no longer stay disconnected after an outage longer than 15 seconds, and repeated drops now back off, up to 30s. Tool descriptions loaded through tool search are cut at 16,384 characters instead of 2,048. On WebSocket servers, a message over 16 MiB now closes the connection.
Subagents preload at most 32 skills from the skills field, each once, and a subagent with the Skill tool can still invoke the rest. The terminal no longer freezes and ignores ctrl+c when a response runs to tens of thousands of lines.
The changelog gives onFailure a single line and leaves real questions open. It names only command and HTTP hooks, does not list which events respect the setting, and does not say whether managed settings can enforce it. In our view, making it opt-in is the right call: a fail-closed hook pointed at a flaky HTTP endpoint will block the agent every time that endpoint times out.
Choosing which guards fail closed
The release notes do not say whether onFailure will reach other hook types, or when the hooks documentation will describe it in more detail. For now, the decision is yours. Pick the guard hooks whose silence you cannot afford and set onFailure: "block" on them. Then test what happens when the script is missing or the endpoint stops answering, before an agent finds out for you.
Related stories
- In Claude Code, a "block" hook could let commands through
- Claude Code 2.1.292 gives sub-agents their own effort dial
- Claude Code 2.1.290 gives mod hooks the subagent's ID
- A Claude Code sideagent now flags what you might miss
- Claude Code 2.1.288 brings back the prompt you Ctrl+C'd
- Claude Code ships mods, the same tool behind its /diff
Comments
No comments yet. Be the first.
Join the conversation
Sign in with Google to leave a comment. Your name and avatar come from your Google profile, and the comment appears after moderation.
