[Bug] Hook Runner Displays False "hook error" for Successful Silent Exits
Bug Description
Bug Report: Hook Runner Generates False "hook error" Messages for Clean Plugin Exits
From: Jordan Dea-Mattson
GitHub: @jordan-of (OrdinaryFolk), @jordandm (personal)
Email: jordan-of@users.noreply.github.com
Related: #35587 (hookify "hook error" for non-matching events), #35575 (disabled plugins register hooks and cause errors)
Problem
Claude Code's hook execution layer displays "hook error" messages even when the hook script completes successfully (exit code 0, no output, no stderr). This affects every hook event type:
UserPromptSubmit, PostToolUse, PreToolUse, SessionStart, and Stop.
The error appears on every prompt submission:
Questions?
⎿ UserPromptSubmit hook error
And on session lifecycle events:
/exit
⎿ Bye!
⎿ SessionStart:resume hook error
Diagnostic Evidence
Invoking the hookify plugin script directly produces a clean result:
echo '{"user_prompt":"test","hook_event_name":"UserPromptSubmit"}' | \
CLAUDE_PLUGIN_ROOT="..." python3 .../hooks/userpromptsubmit.py
# Exit code: 0, stdout: (empty), stderr: (empty)
The script correctly finds no matching rules (none of our hookify rules target the prompt event), returns no output, and exits 0. This is the correct behavior — no matching rules means no output.
Yet Claude Code displays "hook error" anyway.
Root Cause Hypothesis
The hook runner appears to misinterpret one of these as an error:
- Empty stdout — the script produces no output (correct for no matches), but the runner may expect JSON and treat empty as an error
- Python startup time — Python cold start may exceed an internal timeout threshold that's shorter than the configured timeout value
- Process lifecycle issue — a race condition between the script exiting and the runner reading stdout
Impact
This is not just cosmetic. The constant "hook error" messages:
- Desensitize users to real errors — when every prompt shows "hook error," users stop reading error messages. Real hook failures become invisible.
- Undermine trust in the hook system — users who see persistent errors assume hooks are broken and may disable them
- Pollute the conversation — every UserPromptSubmit hook error is injected into the conversation context, consuming tokens and adding noise
Expected Behavior
When a hook script exits 0 with no output:
- No error message should be displayed
- The hook should be treated as "passed silently" — the script had nothing to report
- Only display "hook error" when the script exits non-zero OR produces output that can't be parsed as valid JSON
Environment
- Claude Code version: 2.1.80
- Hookify plugin: claude-plugins-official marketplace
- All hookify rules are event: bash — no prompt or all rules exist
- Tested on macOS Darwin 25.2.0, zsh
- Occurs in both main checkout and valid worktrees
Environment Info
- Platform: darwin
- Terminal: iTerm.app
- Version: 2.1.81
- Feedback ID: f275066a-0ac1-4070-b7a9-1a37618a501f
Errors
[{"error":"Error: NON-FATAL: Lock acquisition failed for /Users/jordan_of/.local/share/claude/versions/2.1.81 (expected in multi-process scenarios)\n at TET (/$bunfs/root/src/entrypoints/cli.js:2724:2174)\n at fgq (/$bunfs/root/src/entrypoints/cli.js:2724:1318)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-03-21T03:43:37.846Z"}]This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗