Hook output display doesn't distinguish between blocking (exit 2) and failure (exit 1)
Description
When using PreToolUse/PostToolUse hooks, the Claude Code UI displays PreToolUse:Write hook error and PostToolUse:Write hook error for all non-zero exit codes. There's no way to distinguish between:
- A hook that intentionally blocked a tool call (exit 2) — working as designed
- A hook that crashed/failed (exit 1) — actual error needing attention
- A hook that exited 0 but wrote to stderr — informational output
All three show the same hook error label in the UI.
Expected behavior
The display should differentiate between these cases. For example:
| Exit code | Current display | Suggested display |
|-----------|----------------|-------------------|
| 0 (with stderr) | hook error | Nothing (or verbose only) |
| 1 (failure) | hook error | hook error (actual error) |
| 2 (blocking) | hook error | hook blocked or hook: <stderr message> |
Reproduction
- Create a PreToolUse hook on
Edit|Writethat intentionally blocks viaexit 2:
#!/bin/bash
echo "Blocked: reason here" >&2
exit 2
- Create a PostToolUse hook on
Edit|Writethat logs informational output:
#!/bin/bash
echo "Audit: file written" >&2
exit 0
- Trigger a Write tool call — both show as
hook errorin the UI
Why this matters
When using hooks heavily for enforcement gates (ARIA gates, session lock checks, proprietary content checks, etc.), seeing hook error on every tool call makes it impossible to tell at a glance whether hooks are working correctly or broken. Users end up ignoring the errors entirely, which defeats the purpose of the error display.
Environment
- Claude Code version: 2.1.74
- OS: Fedora (Bazzite) Linux 6.17.7
- Shell: bash
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗