Environment
@anthropic-ai/claude-agent-sdk 0.3.211
- Node v22.22.0
- Windows 11 Pro (10.0.26100)
- SDK embedded in a long-running Node server; sessions created via
query() with a canUseTool callback and hooks (SubagentStart/SubagentStop, etc.)
Summary
When the main query emits its result message while background Task subagents are still running, the permission control channel is torn down. Every subsequent permission-gated tool call made by those subagents fails with:
Tool permission request failed: AbortError: Stream closed
The subagents themselves keep running (their SubagentStop hooks and task notifications still arrive afterward), but they can no longer execute any tool that needs a canUseTool decision, so they can only fail or produce degraded output. The host application's canUseTool callback is never invoked for these calls — nothing is denied; the request simply has no channel to travel over.
Reproduction scenario
- Start a
query() (streaming input mode) with a canUseTool callback that approves tool use.
- Give the agent a prompt that makes it spawn several parallel background Task subagents and then end its turn to wait for them (e.g. "spawn four reviewer subagents, wait for their reports").
- The main conversation loop finishes its turn → SDK emits
result.
- The still-running subagents' subsequent
Bash/Read/etc. calls now fail with Tool permission request failed: AbortError: Stream closed.
Evidence from captured sessions
We analyzed the persisted message logs of 6 affected sessions (an automated code-review pipeline that spawns 4 parallel reviewer subagents per session):
- Every
Stream closed tool failure carries a parent_tool_use_id (i.e. is a subagent call); the main agent's own calls never fail this way. Example counts per session (subagent failures / main-agent failures): 10/0, 5/0, 6/0, 6/0, 18/0.
- The first failure consistently appears at the exact point the main agent ends its turn to wait for the subagents.
- The host's
canUseTool callback is never invoked for the failing calls (no permission events emitted app-side).
- Subagent calls that do not require a permission round-trip (e.g. allowed CLI-side by settings rules) continue to succeed after the channel closes — confirming the subagent processes are alive and only the permission relay is gone.
system/task_notification messages and SubagentStop hooks for those subagents still arrive minutes after the main result, on the same (still open) message stream.
Expected behavior
The canUseTool control channel should remain usable as long as any subagent of the session is still running — or at minimum until the query stream itself is closed by the SDK consumer. Alternatively, result should not be emitted while background subagents hold pending work (or the docs should state that permission relaying ends at result).
Impact
For autonomous/headless pipelines this is a hard failure mode: the main agent yields to wait for subagents, the session looks "complete" to the host, and the subagents silently lose the ability to run permission-gated tools. In our case review sessions burned ~$2 each across repeated retries without ever posting their output.
Workaround
Passing the needed tools via options.allowedTools (so no canUseTool round-trip is required) makes subagents immune to the channel closure. Copying permission allowlist settings into the working directory achieves the same CLI-side.
Environment
@anthropic-ai/claude-agent-sdk0.3.211query()with acanUseToolcallback and hooks (SubagentStart/SubagentStop, etc.)Summary
When the main query emits its
resultmessage while background Task subagents are still running, the permission control channel is torn down. Every subsequent permission-gated tool call made by those subagents fails with:The subagents themselves keep running (their
SubagentStophooks and task notifications still arrive afterward), but they can no longer execute any tool that needs acanUseTooldecision, so they can only fail or produce degraded output. The host application'scanUseToolcallback is never invoked for these calls — nothing is denied; the request simply has no channel to travel over.Reproduction scenario
query()(streaming input mode) with acanUseToolcallback that approves tool use.result.Bash/Read/etc. calls now fail withTool permission request failed: AbortError: Stream closed.Evidence from captured sessions
We analyzed the persisted message logs of 6 affected sessions (an automated code-review pipeline that spawns 4 parallel reviewer subagents per session):
Stream closedtool failure carries aparent_tool_use_id(i.e. is a subagent call); the main agent's own calls never fail this way. Example counts per session (subagent failures / main-agent failures): 10/0, 5/0, 6/0, 6/0, 18/0.canUseToolcallback is never invoked for the failing calls (no permission events emitted app-side).system/task_notificationmessages andSubagentStophooks for those subagents still arrive minutes after the mainresult, on the same (still open) message stream.Expected behavior
The
canUseToolcontrol channel should remain usable as long as any subagent of the session is still running — or at minimum until the query stream itself is closed by the SDK consumer. Alternatively,resultshould not be emitted while background subagents hold pending work (or the docs should state that permission relaying ends atresult).Impact
For autonomous/headless pipelines this is a hard failure mode: the main agent yields to wait for subagents, the session looks "complete" to the host, and the subagents silently lose the ability to run permission-gated tools. In our case review sessions burned ~$2 each across repeated retries without ever posting their output.
Workaround
Passing the needed tools via
options.allowedTools(so nocanUseToolround-trip is required) makes subagents immune to the channel closure. Copying permission allowlist settings into the working directory achieves the same CLI-side.