[UX] Distinguish 'Stop hook execution error' from 'Stop hook objection' when hook returns ok:false
Summary
When a Stop hook executes successfully and returns {"ok": false, "reason": "..."}, Claude Code surfaces the message as "Stop hook error:" in the transcript. This conflates two distinct outcomes:
- Hook execution error — the hook script crashed, timed out, or returned malformed JSON
- Hook objection — the hook ran fine and reported a policy violation in the model's response
Both currently render under the same "error" label, which is misleading. Case #2 is the intended success path for a review-style hook.
Repro
Configure a Stop hook that returns {"ok": false, "reason": "..."}:
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "prompt",
"prompt": "Respond with {\"ok\": false, \"reason\": \"test\"}",
"model": "claude-haiku-4-5-20251001"
}]
}]
}
}
Trigger any assistant turn. Observe:
Ran 1 stop hook (ctrl+o to expand)
⎿ Stop hook error: ... : test
Expected
Two distinct labels:
- "Stop hook error:" — execution failure (non-zero exit, timeout, malformed output)
- "Stop hook violation:" (or "Stop hook objection") — successful run,
ok: falsereturned
Why this matters
Review/verification hooks (security checks, CLAUDE.md policy enforcement, verify-after-complete patterns) are a common Stop hook use case. Their purpose is to return ok: false when they catch something. Labeling that path as "error" makes correct hook behavior look like a bug to users and conflates real script failures with policy reports — both in the UI and in any log scraping users do.
Environment
- Claude Code CLI
- macOS (Darwin 25.3.0) reported, but affects all Stop hook types (prompt and command) regardless of OS
Credit
Originally reported by @harixth at https://github.com/faizkhairi/claude-code-blueprint/issues/3 — filing upstream here because the fix belongs in the CLI, not in a downstream template repo.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗