[BUG] Hook callback errors include minified JavaScript source code instead of clean error messages

Resolved 💬 5 comments Opened Dec 30, 2025 by nshkrdotcom Closed Feb 14, 2026

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

  1. Start Claude CLI in stream-json mode with hooks configured
  2. Send an initialize request that registers PreToolUse hooks
  3. Send a user message that triggers a tool use (e.g., Bash)
  4. Wait for the CLI to send hook callback requests
  5. Close the input stream WITHOUT responding to the hook callbacks
  6. 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.

View original on GitHub ↗

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