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
- Register a
PreToolUseBash hook with notimeoutfield in~/.claude/settings.json. - In the same turn, fan out 3+ parallel
Bashtool calls. - Trigger a
SessionStart:resumeevent mid-turn (e.g., from a plugin hook) while calls are in-flight. - One of the tool calls fails with:
````
Tool permission request failed: Error: Tool permission stream closed before response received
- 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
- Docs: state explicitly that \
PreToolUse\hooks without \timeout\can stall the permission channel and recommend a default (e.g., 3s). - Harness: apply a default timeout (e.g., 30s) to any hook missing one, instead of unbounded.
- 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.
- 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).
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗