[BUG] UserPromptSubmit hook with hookSpecificOutput JSON shows error on first message of new session

Resolved 💬 3 comments Opened Jan 11, 2026 by greghughespdx Closed Feb 25, 2026

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:

  1. File logging confirms hook execution with correct session_id
  2. Exit code is 0
  3. JSON output is valid (tested with jq)
  4. No stderr output
  5. Same hook works on second message of same session
  6. Same hook works after /compact or /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):

  1. User discovers SessionStart hook output is ignored on new sessions (#10373)
  2. User moves context injection to UserPromptSubmit as workaround
  3. User hits THIS bug - hookSpecificOutput errors on first message
  4. 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.

View original on GitHub ↗

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