PreToolUse hook race causes "Tool permission stream closed before response received" when hook lacks timeout

Resolved 💬 3 comments Opened Apr 6, 2026 by todie Closed Apr 10, 2026
_Filed via Claude Code — the investigation, repro, and draft were produced by the agent itself during an interactive session after hitting this error. Posting with my approval; happy to provide raw session logs on request._

Summary

When a PreToolUse hook has no timeout configured and stalls (even briefly), an in-flight tool call can fail with Tool permission stream closed before response received. The error is opaque — users can't tell whether it's IDE disconnection, hook failure, or something else.

Environment

  • Claude Code 2.1.92 (VSCode extension)
  • Platform: WSL2 (Ubuntu on Windows)
  • Shell: bash

Repro

  1. Register a PreToolUse Bash hook with no timeout field in ~/.claude/settings.json.
  2. In the same turn, fan out 3+ parallel Bash tool calls.
  3. Trigger a SessionStart:resume event mid-turn (e.g., from a plugin hook) while calls are in-flight.
  4. One of the tool calls fails with:

``
Tool permission request failed: Error: Tool permission stream closed before response received
``

  1. The failing command is explicitly in permissions.allow, so it is not a user-approval issue.

Expected

  • Either: in-flight permission requests survive the permission-IPC reconnect, or
  • Error message names the actual cause (hook timeout, IDE disconnect, IPC reset) so users can diagnose.

Actual

Generic \"stream closed\" error. No indication it's hook-related. Local fix is to add \\"timeout\": 3\ to every \PreToolUse\ hook — but this is silent tribal knowledge, not documented.

Suggested fixes

  1. Docs: state explicitly that \PreToolUse\ hooks without \timeout\ can stall the permission channel and recommend a default (e.g., 3s).
  2. Harness: apply a default timeout (e.g., 30s) to any hook missing one, instead of unbounded.
  3. Error surface: distinguish \"IPC closed during user-prompt wait\" from \"hook exceeded timeout\" from \"hook emitted invalid JSON\" — these are three different bugs with three different fixes.
  4. Resilience: if the permission stream closes during an in-flight request for an allow-listed command, retry locally instead of surfacing the error.

Related

anthropics/claude-code#35077 (different trigger, same failure class).

View original on GitHub ↗

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