Distinguish hook denial from hook error in PreToolUse output

Resolved 💬 7 comments Opened Mar 6, 2026 by r0bbbb Closed Apr 18, 2026

Problem

When a PreToolUse hook intentionally blocks a command (exit code 2), the output is labeled:

PreToolUse:Bash hook error: [message]

This is misleading — the hook didn't error, it intentionally denied the operation. There's no way to distinguish between "the hook crashed" and "the hook blocked this command by design."

Use case

We use PreToolUse hooks as a CLI governance layer — enforcing read-only AWS access, blocking destructive git operations, preventing production writes, etc. These are intentional policy denials, not errors. The "hook error" label confuses users into thinking something is broken.

Proposed solution

Differentiate the label based on exit code or hook output:

  • Exit 2 (blocking denial): PreToolUse:Bash hook denied: [stderr message]
  • Non-zero other (actual error): PreToolUse:Bash hook error: [stderr message]

Alternatively, allow hooks to set a custom label via structured JSON output (e.g., {"label": "BLOCKED", "message": "..."}).

Current workaround

We prefix our stderr messages with BLOCKED: to make intent clear, but the outer "hook error" label still appears and causes confusion.

View original on GitHub ↗

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