Subagent lifecycle mishandles long external waits: sleep-block guidance prescribes Monitor, 'completed' fires with live Monitor children, terminal events dropped

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 12, 2026

Summary

Three interacting defects in how Claude Code handles a subagent ("sub-task"/background agent
spawned by a parent session) that needs to wait on a long-running external condition (in our case,
GitHub Actions CI checks that can run past 10 minutes). Individually each behavior might be
defensible; together they make it structurally impossible for a subagent to correctly wait on
anything that outlives a single tool call, while giving the agent no signal that this is what
happened. We hit this six times in one session, at a cost of roughly 1.2M wasted subagent tokens,
and it produced one near-miss where an agent's un-run final step was a message to a human
teammate that would have prevented him from wasting his own time.

Environment: Claude Code CLI, observed 2026-08-11. Agents are dispatched as subagents/sub-tasks
of a parent (orchestrator) session and report status back to it via task notifications
(<task-notification> events carrying a status field).

Defect 1 — the blocked-command error message prescribes an unsafe-for-subagents pattern

When a subagent runs a command shaped like a manual wait loop — e.g. sleep 90; <check-something>,
or a for-loop of short sleeps — the harness blocks it with a tool-use error resembling:

Blocked: sleep 90 followed by: <command> ...
To wait for a condition, use Monitor with an until-loop (e.g. `until <check>; do sleep 2; done`)
— you get a notification when the loop exits. Do not chain shorter sleeps to work around this
block.

This is good advice for the persistent parent/orchestrator session, where a Monitor can run
across turns and its events are delivered directly to the session that armed it. It is actively
harmful advice inside a subagent, because of Defects 2 and 3 below: a subagent that follows
this exact instruction ends up idle-armed, gets reported to its parent as completed, and can miss
its own monitor's terminal event. The error message has no way to know it's running inside a
subagent, but the prescribed remedy is only safe in one of the two contexts it can fire in.

Minimal repro shape: dispatch a subagent with a task that includes a genuine long-running
external wait (anything exceeding the ~10-minute background-command cap). Have it attempt an inline
polling loop; observe the block; have it follow the suggested remedy (arm a Monitor, end the
turn). Proceed to Defects 2–3.

Defect 2 — status: completed is reported to the parent while the subagent has a live Monitor child

Observed directly in transcripts: for a single subagent, three separate task notifications arrived
at the parent session with <status>completed</status> (at three different timestamps roughly a
minute apart), each occurring immediately after the subagent ended a turn with plain text (e.g.
"Holding for the monitor.") while its own armed Monitor was still running and had not yet fired.

The parent session has no way to distinguish "this subagent is genuinely finished" from "this
subagent is idle-armed, correctly waiting on its own live child, and will resume when that child's
event lands." Both produce the exact same completed status and a calm, plausible-sounding text
summary. In our environment this is dangerous specifically because the standing operating
convention is "never leave a subagent open-endedly waiting" — so a completed status is trusted as
"nothing more will happen here," and a parent that trusts it will either re-dispatch fresh work
(orphaning the live monitor) or simply move on, silently dropping whatever the subagent still
intended to do once woken.

We also confirmed the converse false-positive: a subagent that is legitimately still working —
blocked on its own delegated sub-agent rather than a Monitor — produces what looks like the same
"went idle" signal to an outside observer polling for progress. We did not fully isolate whether
this is the identical code path as the completed-with-live-Monitor case, but the symptom (a
task that looks finished/idle while genuinely still working) recurred in two different shapes in
the same session, which suggests a general lifecycle-signaling gap around "the agent is idle but
not done," not one localized bug.

Impact: a status field the parent is meant to trust ("completed") is asserted while the
condition it implies (no more work will happen from this agent) is false. This is the crux of the
whole report — everything else follows from it.

Defect 3 — a Monitor's terminal event can be dropped after its subagent is marked completed

In one traced case, the subagent's own Monitor correctly detected the awaited condition
(a CI check reaching a terminal conclusion state) and emitted its terminal event roughly two
minutes AFTER the subagent's last turn (the one that produced the completed status in Defect 2).
Nothing woke as a result. The event was not delivered to the parent, and the subagent was not
resumed. The only reason this was caught at all was that a human operator was manually polling the
underlying external system (GitHub) in parallel and noticed the discrepancy.

We could not determine from the outside whether this is (a) the event firing into a subagent
context that the harness has already torn down or deprioritized once completed was reported, or
(b) a race between the monitor thread and the status-reporting thread that only sometimes
resolves correctly (three earlier wake-ups for the same agent in the same session DID reach it and
were acted on — see below). Either way, the effect is that "arm a Monitor and wait" is not a
reliable pattern inside a subagent even though it worked most of the time in the same session.

Notably, the same subagent's Monitor DID successfully wake it three other times earlier in the
same run (each time it responded and continued). So this is not "Monitor never wakes a subagent" —
it is closer to "Monitor wakes a subagent unreliably once the subagent has already been reported
completed at least once," which is a narrower and more concerning claim: the failure mode appears
to compound with Defect 2 rather than being independent of it.

Why this combination is dangerous rather than merely inconvenient

The agent cannot distinguish, from the inside, between:

  • a text-only turn that is a status narration ("still waiting, monitor armed") meant to keep

the parent informed while work continues, and

  • a text-only turn that is the actual final answer to the task.

Both look identical to the harness's completion detector: a turn that ends with plain text and no
further tool calls. This means the safe instruction to give a subagent — "give a status update
while you wait" — is indistinguishable, at the protocol level, from ending the task. There is no
way to author a subagent brief that avoids this ambiguity from the inside; the fix has to come from
outside (never letting a subagent own the wait at all) or from the harness (a status update and a
completion need genuinely different signals, not the same “turn ended with text” heuristic).

Impact observed in one production session

  • Six stalls, one session, ~1.2M subagent tokens spent (each stall's resume re-established context

the agent had already built).

  • One near-miss: an agent's un-executed final step was posting a status update to a human

teammate's pull request telling him his work had already landed elsewhere. The stall meant the
most recent visible comment on his PR was earlier (and by then incorrect) advice to rebase.
Caught only because a human operator happened to check before the teammate acted on stale
guidance.

  • A separate, related false-positive: an idle/available signal fired for an agent that was, in

fact, still working (waiting on its own delegated sub-agent), 38 seconds before that agent
produced a real commit. An external observer treating "idle" as "stalled" would have interrupted
genuinely-in-progress work.

What we changed on our side (for context, not to imply the fix belongs there)

We restructured our own agent briefs so that no subagent is ever asked to own a wait longer than a
single bounded tool call can cover, moved all open-ended CI waiting to the persistent parent
session's own Monitor (which behaves reliably — the same session type does not exhibit Defects
2–3), and required every subagent's report to open with an explicit DONE / INCOMPLETE /
HANDBACK marker so a parent never has to infer completion from a calm-sounding summary. These are
workarounds for our own workflow; they do not address the underlying ambiguity between a status
narration and a final answer, or the apparent unreliability of Monitor delivery once a subagent has
already been reported completed once.

Suggested areas to investigate

  1. Whether status: completed can be deferred, or reported as a distinct state (e.g.

idle-with-live-children), whenever a subagent ends its turn with at least one still-running
background construct (Monitor, background command) that it itself armed.

  1. Whether the blocked-command error message can detect that it's firing inside a subagent context

and either omit the Monitor suggestion or add an explicit caveat that Monitor is
parent-session-safe only.

  1. Whether the Monitor-event-to-subagent delivery path has a race with the completion-reporting

path — specifically, whether an event that fires after a completed status has already been
sent can be silently lost rather than either delivered or explicitly reported as dropped.

View original on GitHub ↗