Skip to content

fix(sdk): don't end the run while background subagents are still running (#1499)#1500

Open
conancain wants to merge 1 commit into
anthropics:mainfrom
conancain:fix/background-subagent-loop-termination
Open

fix(sdk): don't end the run while background subagents are still running (#1499)#1500
conancain wants to merge 1 commit into
anthropics:mainfrom
conancain:fix/background-subagent-loop-termination

Conversation

@conancain

@conancain conancain commented Jul 13, 2026

Copy link
Copy Markdown

Summary

runClaudeWithSdk (base-action/src/run-claude-sdk.ts) breaks the query() loop on the first result message — added in #1339 to avoid a pull_request hang. But subagents launched via the Agent tool run as background tasks by default (Claude Code ≥ 2.1.198), so the parent agent's dispatch turn emits a result while the subagents are still running. Breaking on it ends the run and orphans them, and the parent never gets the follow-up turn(s) to consume their output. Full write-up in #1499.
This tracks the live background-task set via the background_tasks_changed system message and only breaks once no background tasks remain live. The SDK's async-agent stall watchdog bounds the wait if a subagent never completes, so the #1339 anti-hang guard is preserved. With no background subagents (e.g. tag mode) the behavior is unchanged — break on the first result.

Fixes #1499

Known limitation

This fixes the primary failure (terminating on the first result while subagents run). A narrower race can remain: when multiple subagents finish near-simultaneously, the live set can drain to empty while a per-task follow-up turn (the one that consolidates) is still queued, so the loop may break on an interim follow-up result before the final turn. Fully closing that likely needs SDK-level knowledge of whether more follow-up turns are pending (discussed in #1499). Happy to iterate if you'd prefer a different approach.

runClaudeWithSdk breaks the query() loop on the first result message (added
in anthropics#1339 to avoid a pull_request hang). Subagents launched via the Agent
tool run as background tasks by default (Claude Code >= 2.1.198), so the
parent agent's dispatch turn emits a result while the subagents are still
running. Breaking on it ends the run and orphans them, so the parent never
gets the follow-up turn(s) to consume their output.

Track the live background-task set via background_tasks_changed and only
break once no background tasks remain live. The SDK's async-agent stall
watchdog bounds the wait if a subagent never completes, preserving the
anthropics#1339 anti-hang guard.

Addresses anthropics#1499

Co-authored-by: Cursor <cursoragent@cursor.com>
JamesScrase-PortSwigger added a commit to JamesScrase-PortSwigger/claude-code-action that referenced this pull request Jul 16, 2026
Builds on anthropics#1500. anthropics#1500 keeps the query() loop open while background tasks
are live, but a string/one-shot prompt closes the CLI's stdin immediately,
and in print mode a background Bash shell is reaped ~5s after the final
result once stdin has closed. Subagents are exempt from that reap; bash is
not — so anthropics#1500 alone does not save a long background bash (e.g. a test
suite), which is always killed before it finishes.

Feed the prompt as a stream and hold the generator open until the live
background set (reported by `background_tasks_changed`) drains to zero,
keeping the process off its exit path. Background bash then runs to
completion and the model receives its follow-up turn(s) — matching
interactive behavior. The no-background path is unchanged (break on the
first result with an empty live set), preserving the anthropics#1339 anti-hang guard.

Validated locally against runClaudeWithSdk: a run_in_background sleep-12
Bash task is reaped (never completes) on anthropics#1500-only, and runs to completion
on this change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Background subagents orphaned: runClaudeWithSdk breaks the query() loop on the first result (regression from #1339)

1 participant