A PreToolUse policy *deny* is surfaced to the model as a "hook error"

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 10, 2026

Type: feature-request

Environment

  • Claude Code version: 2.1.220
  • OS: macOS 26.5.2 (Darwin 25.5.0)
  • Platform: Claude subscription

What happened

When a PreToolUse hook deliberately denies a tool call (exit 2, with an explanation on stderr), the model receives it as:

PreToolUse:<ToolName> hook error: [<path to hook>]: <explanation>

The word error is doing the wrong job here. A deny is a policy decision with a stable outcome; an error implies something went wrong and might not go wrong next time. In our transcripts this framing appears in 263 of 5 096 transcript files, and it visibly nudges the model toward retrying — sometimes with a slightly reworded payload, which fails identically because the policy has not changed.

It also makes real hook failures indistinguishable from intentional denies: a hook that crashes on a syntax error and a hook that refuses a call both surface as "hook error", which makes debugging a hook setup harder than it needs to be.

Expected

Distinguish the two outcomes in the message the model sees, for example:

  • deny → Blocked by policy (PreToolUse:<ToolName>, <hook name>): <reason> — plus, ideally, a hint that retrying the same call will produce the same result.
  • hook crash / non-zero exit that is not a deny → Hook failed (PreToolUse:<ToolName>, <hook name>): <stderr>.

A structured field in the hook's JSON output (decision: "deny" vs. an unexpected failure) would let the harness label these correctly without guessing.

Repro

  1. Register a PreToolUse hook that exits 2 with an explanatory message on stderr for some tool.
  2. Trigger that tool.
  3. Observe the model receives PreToolUse:<Tool> hook error: ... and frequently retries the same call.

Impact

Wasted retries on decisions that are deterministic by construction, and a harder time telling a working policy hook from a broken one. Low implementation cost — this is a wording and classification change, not new machinery.

View original on GitHub ↗