[BUG] UserPromptSubmit hook with hookSpecificOutput JSON shows error on first message of new session
Describe the bug
UserPromptSubmit hooks that output hookSpecificOutput JSON display "UserPromptSubmit hook error" on the first message only of a new session. The hook executes successfully (verified via logging), but the error is shown to the user. Plain text output works correctly as a workaround.
Claude Code version: 2.1.4
OS: macOS (Darwin 25.3.0)
---
Detailed Test Results
| Hook Output | First Message | Subsequent Messages |
|-------------|---------------|---------------------|
| {} (empty JSON) | ✅ No error | ✅ No error |
| {"hookSpecificOutput":{"additionalContext":"test"}} | ❌ Shows error | ✅ No error |
| Plain text: My context here | ✅ No error, context injected | ✅ No error |
Key finding: The error appears ONLY on the first UserPromptSubmit of a new session when using hookSpecificOutput JSON format.
---
Reproduction Steps
1. Create test hook at ~/.claude/hooks/test-userpromptsubmit.sh:
#!/bin/bash
INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // empty')
# Log to prove hook executes
echo "$(date -Iseconds) Hook fired for session $SESSION_ID" >> /tmp/hook-debug.log
# This output format causes the error on first message:
echo '{"hookSpecificOutput":{"additionalContext":"Test context injection"}}'
exit 0
2. Make executable:
chmod +x ~/.claude/hooks/test-userpromptsubmit.sh
3. Add to ~/.claude/settings.json:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "$HOME/.claude/hooks/test-userpromptsubmit.sh",
"timeout": 10
}
]
}
]
}
}
4. Test:
# Clear any cached state
rm -f /tmp/hook-debug.log
# Start NEW Claude Code session
claude
# Type any message, press enter
Expected: Hook output processed, context injected, no error
Actual: "UserPromptSubmit hook error" displayed below the message
5. Verify hook actually succeeded:
cat /tmp/hook-debug.log
# Shows: 2026-01-11T... Hook fired for session abc-123...
---
Evidence That Hook Succeeds
The hook runs correctly - the error is in how Claude Code handles the response on first message:
- File logging confirms hook execution with correct session_id
- Exit code is 0
- JSON output is valid (tested with
jq) - No stderr output
- Same hook works on second message of same session
- Same hook works after
/compactor/clear
---
Workaround
Use plain text output instead of JSON hookSpecificOutput:
#!/bin/bash
cat > /dev/null
# Instead of JSON:
# echo '{"hookSpecificOutput":{"additionalContext":"My context"}}'
# Use plain text (works without error):
echo 'My context to inject'
exit 0
Per the docs, plain text stdout is also added as context for UserPromptSubmit hooks.
---
Interaction with Other Bugs
This bug creates a problem for users working around #10373 (SessionStart hooks not injecting on new sessions):
- User discovers SessionStart hook output is ignored on new sessions (#10373)
- User moves context injection to UserPromptSubmit as workaround
- User hits THIS bug - hookSpecificOutput errors on first message
- User must use plain text output as double-workaround
---
Related Issues
- #10373 - SessionStart hooks not working for new conversations (OPEN)
- #13912 - UserPromptSubmit stdout causes error (CLOSED as dup of #12151)
- That was v2.0.69 where ALL stdout errored
- Current v2.1.4 behavior suggests partial fix: plain text works, JSON hookSpecificOutput still fails on first message
- #12151 - Plugin hooks output not captured (OPEN, but about plugins not settings.json hooks)
---
Suggested Investigation
The issue appears to be in how the first UserPromptSubmit of a session handles JSON parsing or hookSpecificOutput extraction. Since:
- Empty JSON
{}works - Plain text works
- hookSpecificOutput JSON fails only on first message
- Same hookSpecificOutput JSON works on subsequent messages
...the initialization path for new sessions may be missing setup that subsequent messages have.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗