Skip to content

canUseTool control channel closes when main query emits result while background Task subagents still running (subagent tools fail with 'AbortError: Stream closed') #384

Description

@JWDavey

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

  1. Start a query() (streaming input mode) with a canUseTool callback that approves tool use.
  2. 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").
  3. The main conversation loop finishes its turn → SDK emits result.
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingduplicateThis issue or pull request already exists

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions