Hook output display doesn't distinguish between blocking (exit 2) and failure (exit 1)

Resolved 💬 3 comments Opened Mar 16, 2026 by OpsKern Closed Apr 23, 2026

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:

  1. A hook that intentionally blocked a tool call (exit 2) — working as designed
  2. A hook that crashed/failed (exit 1) — actual error needing attention
  3. 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

  1. Create a PreToolUse hook on Edit|Write that intentionally blocks via exit 2:
#!/bin/bash
echo "Blocked: reason here" >&2
exit 2
  1. Create a PostToolUse hook on Edit|Write that logs informational output:
#!/bin/bash
echo "Audit: file written" >&2
exit 0
  1. Trigger a Write tool call — both show as hook error in 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

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗