Repository navigation
t3_background_wait accepts tasks owned by a foreground native subagent, which are killed when it ends its turn, so the subagent stalls silently #24956
Description
What happened
look at the stalled subagent in this thread and figure out why it was allowed to happen. i also notice agents running commands in the background and monitoring them just show up as done in t3 code, including sub agents, so even though a sub agent is monitoring something and will wake up, t3 code shows everything as done
A native Claude subagent (an implementor in its own worktree) started a long test run in the background, called t3_background_wait on it, and ended its turn, saying "I'll pick up again when they report." It never woke up. The test run and its Monitor were killed in the same second the subagent ended. T3 showed the subagent and the thread as done. The parent passed the subagent's "I'll pick up again" on to the user, and nothing caught the stall until the user asked 16 minutes later.
Diagnosis
T3's instructions to Claude contradict each other inside a foreground native subagent, and t3_background_wait accepts a wait that can't happen.
packages/provider-core/src/server/orchestrationInstructions.ts:9tells Claude to run a nativeAgent"always withrun_in_background:false". So the subagent is foreground. When a foreground subagent ends its turn, the subagent is finished, and Claude Code kills the background Bash tasks and Monitors it owns (task_updated {status: "killed"}, 2 ms before the subagent's owncompleted).CLAUDE_BACKGROUND_WAIT_INSTRUCTIONS(apps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts:1307) says: "To wait for a background command or Monitor you started … callt3_background_waitwith its task ID before you end your turn." Subagents see this too. Inside a foreground subagent, following it guarantees a stall.awaitBackgroundTasks(ClaudeAdapterV2.ts:10231) matches task IDs against the whole session's task list. That list includes tasks owned by a foreground subagent (parent_task_idset,owned_by_subagent: true). So the call returnedmatchedfor tasks that were about to be killed. Nothing tells the agent the wait is impossible.
Related: t3_background_wait also fails from a background native subagent in the same thread:
- A subagent resumed with
SendMessagewas woken by its own background tasks while the parent was between turns. Itst3_background_waitcall was rejected withparent_not_active(isLiveCallerShell,apps/server/src/mcp/OrchestratorMcpService.ts:218). The subagent was live, but T3 still had it as completed, because a resume doesn't start a newAgenttool call. The Lineage panel lists such subagents under "Previous agents" while they're running. - Passing a subagent's own task ID returns
unmatched.
On the user's other complaint (threads show done while commands or Monitors run): this is by design (backgroundWorkKindHoldsCompletion in packages/shared/src/orchestrationV2PendingBackgroundWork.ts). Commands and Monitors hold Working only when marked awaited. In practice the mark rarely happens: 13 of this user's 200 Claude provider sessions ever called t3_background_wait, and every call in the thread investigated here came from a subagent.
Steps to reproduce
- On a Claude thread, ask for a task where the agent spawns a native
Agent(foreground, as T3 instructs) whose work includes a command longer than 10 minutes. A UI test run works; Bash's 600 s limit moves it to the background. - Inside the subagent, the command is backgrounded. The subagent calls
t3_background_waitwith the task ID (getsmatched) and ends its turn. - Observe: the background task and any Monitor get
status: killed. The subagent completes with a "will pick up again" report. The thread shows done. No wake-up ever arrives.
Version
0.1.3-nightly.20261025.3669 (desktop app with local server; triage CLI was 0.1.2)
Environment
macOS 27.2 (arm64), Node 26.12.1, Claude Code 2.1.310 via claude-agent-sdk, claude-opus-5-5
Evidence
17:14:27.610 task_started task_id=a9cfb0c3053a1c025 subagent_type=implementor is_backgrounded=false
17:34:59.785 task_started task_id=bjovbiddl owned_by_subagent=true parent_task_id=a9cfb0c3053a1c025 "Mutation check …"
17:44:56.787 task_updated bjovbiddl {is_backgrounded: true} # Bash 600 s timeout moved it to background
17:45:54.270 task_started task_id=bs384m83p (Monitor) owned_by_subagent=true parent_task_id=a9cfb0c3053a1c025
17:46:05.104 tool_use mcp__t3-code__t3_background_wait {"taskIds":["bjovbiddl","bs384m83p"]} (inside subagent)
17:46:05.163 tool_result {"matched":["bjovbiddl","bs384m83p"],"unmatched":[]}
17:46:07.423 task_updated bjovbiddl {status: "killed"}
17:46:07.452 task_updated bs384m83p {status: "killed"}
17:46:07.454 task_updated a9cfb0c3053a1c025 {status: "completed"}
17:46:07.456 hand-back: "The mutation run and its monitor are both running in the background. I'll pick up again when they report."
18:02:02 parent SendMessage resumes the subagent after the user asks
# Same thread, background (SendMessage-resumed) subagent, parent between turns:
16:29:23.340 task_started task_id=aada0d84ad95d00da is_backgrounded=true # woken by its own task
16:29:41.587 tool_use t3_background_wait {"taskIds":["bryakiqmh","bewduxibb"],"waiting":true}
16:29:41.624 tool_result {"code":"parent_not_active","message":"The calling provider no longer owns an active thread run."}Related issues
#16808 has the same parent_not_active rejection, for delegate_task from a background native subagent. This issue is about the foreground-subagent stall, which #16808 doesn't cover. #19926 (closed) added the Waiting state this relies on.
Possible fixes:
- Reject or warn on
t3_background_waitfor tasks owned by a foreground subagent ("these die when you end your turn; wait in this turn or usedelegate_task"). - Tell subagents in the injected instructions never to end their turn on background work.
- Flag a subagent that completes with killed owned tasks, so the parent and the UI don't report it as done.
Fix applied or workaround
Parent resumed the subagent with SendMessage; it finished. User-side: instructions now tell foreground subagents to wait in-turn and to send long verification jobs through delegate_task.
Filed by
Claude Code (claude-opus-5-5) via t3 triage
Activity