[BUG] Hook callback errors include minified JavaScript source code instead of clean error messages
Summary
When the SDK/client closes the input stream while hook callbacks are pending, the CLI prints error messages that include thousands of characters of minified JavaScript source code instead of clean, user-friendly error messages.
Expected Behavior
Clean error message like:
Error in hook callback hook_0: Connection closed by client
error: Tool permission stream closed before response received
Actual Behavior
Error includes ~4000+ characters of minified JS with line numbers:
Error in hook callback hook_0: 4819 | `),enablePromptCaching:!0,signal:new AbortController().signal,options:{querySource:"summarize_for_resume",agents:[],isNonInteractiveSession:$,hasAppendSystemPrompt:!1,mcpTools:[]}})).message.content.filter((U)=>U.type==="text").map((U)=>U.text).join("")}function AT1(H){return sgH.join(om(),H.replace(/[^a-zA-Z0-9]/g,"-"))}...
4820 | `);if(I===-1)return JSON.parse(L.trim()).type==="summary";let D=L.substring(0,I);return JSON.parse(D).type==="summary"}catch{return!1}}...
...
error: Tool permission stream closed before response received
at read (/$bunfs/root/claude:4824:219)
at processTicksAndRejections (native:7:39)
The stack trace shows /$bunfs/root/claude:4824:219 which is the Bun-bundled binary leaking internal source context.
Reproduction Script
Save as repro_hook_cleanup_error.sh and run with bash repro_hook_cleanup_error.sh:
#!/bin/bash
#
# Reproduces CLI bug: minified source code in error messages on early disconnect
echo "========================================"
echo "CLI Bug Repro: Minified Source in Errors"
echo "========================================"
echo ""
# Create named pipes for bidirectional communication
STDIN_PIPE=$(mktemp -u)
mkfifo "$STDIN_PIPE"
cleanup() {
rm -f "$STDIN_PIPE"
}
trap cleanup EXIT
echo "Starting Claude CLI with hooks..."
echo ""
# Start Claude CLI in background, reading from our pipe
# Pipe through cat to prevent TTY detection which would launch interactive mode
claude \
--output-format stream-json \
--input-format stream-json \
--verbose \
--allowedTools Bash \
--model haiku \
--permission-mode bypassPermissions \
< "$STDIN_PIPE" 2>&1 | cat &
CLI_PID=$!
# Open the pipe for writing (keeps it open)
exec 3>"$STDIN_PIPE"
# Send initialize request with hooks
echo ">>> Sending initialize request with hooks..."
echo '{"type":"control_request","request_id":"init_1","request":{"subtype":"initialize","hooks":{"PreToolUse":[{"matcher":"Bash","hookCallbackIds":["hook_0","hook_1"]}]}}}' >&3
sleep 1
# Send a user message that will trigger Bash tool use
echo ""
echo ">>> Sending user message to trigger Bash tool..."
echo '{"type":"user","message":{"role":"user","content":"Use the Bash tool to run: echo hello"}}' >&3
# Wait for Claude to process and send hook callback request
echo ""
echo ">>> Waiting for hook callback request (3 seconds)..."
sleep 3
# Now close the pipe WITHOUT responding to the hook callback
echo ""
echo ">>> Closing connection without responding to hook callback..."
echo ""
exec 3>&-
# Wait for CLI to exit and show its errors
wait $CLI_PID 2>/dev/null || true
echo ""
echo "========================================"
echo "If you see 'Error in hook callback' with minified JS code above,"
echo "this confirms the CLI bug."
echo "========================================"
Steps to Reproduce
- Start Claude CLI in stream-json mode with hooks configured
- Send an initialize request that registers PreToolUse hooks
- Send a user message that triggers a tool use (e.g., Bash)
- Wait for the CLI to send hook callback requests
- Close the input stream WITHOUT responding to the hook callbacks
- Observe the error output contains minified source code
Root Cause Analysis
The error appears to originate from the Bun runtime's error formatting. When an error is thrown in the hook callback handling code, the stack trace formatter includes source context from the bundled/minified JavaScript, which results in thousands of characters of unreadable code being printed.
The relevant code path appears to be in the processLine function where pending hook requests are rejected:
for(let $ of this.pendingRequests.values())
$.reject(Error("Tool permission stream closed before response received"))
Impact
- Makes debugging difficult for SDK developers
- Pollutes logs with thousands of characters of noise
- Exposes internal implementation details
- Poor user experience when errors occur
Environment
- Claude Code version: 2.0.76
- Platform: Linux (WSL2)
- Runtime: Bun
Suggested Fix
Consider catching errors from pending hook callbacks and formatting them with clean messages, or configure Bun/the bundler to not include source context in stack traces for production builds.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗