[BUG] Background Bash's "You will be notified when it completes" is false for a subagent that stops — the copy invites orphaning, while agent children correctly resume the parent

Status Fixed / completed
Reported on v2.1.218
Maintainer reply None cached
Activity 3 comments · opened Jul 24, 2026 · closed Sep 10, 2026

Preflight

  • [x] I have searched existing issues; the orphaning itself is reported (#23154, #19045, #72171) but not this angle: the tool copy that induces it, and the shell-task vs agent-child asymmetry
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code (2.1.218)

Summary

A subagent that starts a Bash command with run_in_background: true is shown, verbatim: "Command running in background with ID: … You will be notified when it completes." Its Bash tool description likewise says run_in_background "keeps running across turns and re-invokes you when it exits", and arming Monitor adds "Keep working — do not poll or sleep."

For the main session, all of this is true. For a subagent, none of it survives its stop: emitting final text ends the session, the backgrounded shell command dies with it, and the promised notification never arrives — there is no later turn to deliver into. Nothing in any of these messages distinguishes the two cases, so a subagent with nothing else to do follows the text as written: it backgrounds the long command and ends its turn "to wait". That is precisely obedient behavior producing an orphaned task.

The asymmetry that proves the copy is the problem: for a nested agent child the promise is honest. We verified that a subagent that stops while its own Agent-tool child is still running is kept alive and re-invoked with that child's completion notification (the notification's own footnote documents it: fires "each time this agent stops with no live background children of its own"). So the semantics are: shell background task → dies at parent stop, no notification; agent child → parent session held open and resumed. The user-facing copy claims the second behavior for both.

Reproduction

  1. From a top-level session, launch a general-purpose subagent instructed to: run a ~60s marker-writing shell command with run_in_background: true, quote the tool result it receives, then immediately output its final text "stopping to wait for the notification".
  • Result: the subagent quotes "You will be notified when it completes", stops, and the marker file never completes — the process is gone. No notification ever reaches it. (Reproduced 2× today in production use before the controlled probe: two ~15-minute e2e suite runs orphaned mid-build the moment their subagent stopped "to wait".)
  1. Same experiment with a nested agent child instead of a shell task: subagent launches an Agent-tool child that takes ~90s, immediately stops, and carries an instruction to write a tripwire file if ever re-invoked.
  • Result: ~90s later the parent is resumed by the child's completion notification and writes the tripwire. 1 trial, plus the notification footnote documenting the semantics.
  1. Control (what a subagent can do): foreground Bash accepts timeout up to 3600000ms, and an until <check>; do sleep 2; done loop is permitted (only standalone sleep is blocked) — a 22s foreground await of the background task's completion marker worked inside the probe subagent.

Scope

  • This is about the copy and the undocumented contract, not a request to change lifecycle mechanics (though that would also fix it — see Expected). The docs (tools-reference, sub-agents) document neither what happens to a subagent's background tasks at stop, nor the shell-vs-agent-child asymmetry.
  • Distinct from #23154, which reports subagents lacking TaskStop and orphaning tasks they spawn: here the subagent is induced to orphan by text that promises re-invocation it cannot receive.

Why this matters

Capable models follow tool text literally. The stronger the model, the more reliably "You will be notified when it completes" produces a subagent that backgrounds a 30-minute job and stops — the correct reading of an incorrect promise. Each occurrence silently discards long work: our two production cases each lost a full build-and-test cycle, and the parent session read the stopped agent's final text as an interim progress report, compounding the confusion.

Expected

Any of, in preference order:

  1. Condition the background-Bash copy on session type: in a subagent, say the true thing — e.g. "this task will not survive your final message; await it in-session (foreground timeout, or an until-loop on its output) before handing off."
  2. Extend the agent-child semantics to shell tasks: hold a subagent session open while it has live background tasks and re-invoke it on completion, matching the existing behavior for Agent-tool children.
  3. At minimum, document the contract in sub-agents docs: background shell tasks die at subagent stop; agent children hold the session open.

Related issues

  • #23154 — subagents can orphan their own Bash tasks (mechanics; no copy angle)
  • #19045, #72171 — background/subagent processes orphaned at session end (adjacent lifecycle gaps)

Is this a regression?

I don't know — I haven't tested earlier versions.

Environment

  • Claude Code: 2.1.218
  • Model: claude-fable-5

View original on GitHub ↗

3 Comments

kcarriedo · 2 months ago

The shell-vs-agent-child asymmetry you've documented here is the clearest write-up of this problem I've seen. The two controlled reproduction cases (shell background dies at subagent stop; agent child holds the session open) prove it is a contract difference, not a timing issue, and the production evidence (two full build-and-test cycles lost before the controlled probe) shows the cost.

The framing in point 1 of Expected is the right fix: condition the copy on session type. A subagent needs to know it is a subagent and that the "you will be notified" lifecycle does not apply to it. The model following the text "correctly" is the problem -- capable models read tool descriptions literally, which is exactly what produces this failure reliably.

Two things that may help while the fix lands:

For the foreground-loop workaround you mention in the reproduction section: the until <check>; do sleep 2; done pattern works inside a subagent for waiting on short-to-medium background tasks. For longer builds, structure the subagent prompt to complete a foreground await before emitting final text, rather than relying on re-invocation. It means holding the subagent alive longer, but that is better than the build vanishing.

For the parent session reading the stopped subagent's final text as a progress report: one mitigation is to instruct the orchestrating session to check a known artifact (a file written by the build, a log entry, an exit code written to disk) rather than trusting the subagent's narrative output. The narrative "stopping to wait for the notification" looks like forward progress but is not checkable -- an artifact is.

Neither addresses the root copy problem, but they reduce how often silent orphaning happens in practice.

nohat · 2 months ago

Controlled probes confirming the copy trap (Claude Code 2.1.221, macOS)

Same root cause as this issue. Sharing additive evidence from a scaffold-dev subagent investigation (2026-08-03):

Mode A (obedient wait → unfinished complete): a production subagent ended with "I'll stop making tool calls now and wait for the background task notification…" after run_in_background pytest. Parent received <task-notification><status>completed</status> with that text as <result> — work unfinished. Sleep-guard errors amplify the trap (To wait for a command you started, use run_in_background: true.).

Mode A' (confabulation under forced wait): Probe forced run_in_background + "wait for notification" on a ~45–60s marker command. Task returned in ~4.4s; the subagent invented the expected done marker (PROBE5A_DONE) without ever receiving a completion notification or observing the tool result.

Mitigation that works today (Probe 5b): an explicit brief line contradicting the Bash docs (run_in_background does NOT re-invoke you; backgrounded processes are killed when your turn ends) → foreground blocking Bash, real done ~52s, no stall.

Also adjacent: premature completed notifications whose <result> is mid-sequence narration ("Now write tests…") while tools from the same message.id already ran — false-positive "stall" signals for orchestrators.

Ask preference matches this issue: condition the background-Bash copy (and tool_result "You will be notified…") on session type for subagents; optionally hold/resume like Agent children. Kill-at-return + false notify also tracked in #83627; scheduler gap in #78782.

milobird · 1 month ago

Closing this: the orphaning half is fixed, and I can pin where.

Observed on 2.1.267 (macOS, interactive session, general-purpose subagent). A subagent that started sleep 30 && <write marker> with run_in_background: true and immediately ended its turn was not orphaned: the command ran to completion, the subagent was re-invoked with the completion notification (a [SYSTEM NOTIFICATION - NOT USER INPUT] message), and its second final reached the parent as a second task notification on the same task id. Same result in a headless claude -p --input-format stream-json session with stdin held open. That is "Expected" option 2 above.

Where it landed, from reading the shipped binaries. Each agent task carries a keepalive set with reasons tagged agent:<id>, bash:<id>, monitor:<id>, workflow:<id>. The agent: keepalive is unconditional, which is the asymmetry this issue described. The bash: keepalive for a backgrounded subagent, and routing the command's completion back to that subagent instead of the main conversation, are gated by a GrowthBook feature tengu_concurrent_shore read with default true. That flag and the gated code are absent from 2.1.224 through 2.1.241 and present from 2.1.242 (which has no changelog section). 2.1.243's changelog is the first mention: "Fixed background subagents not waking when their last background Bash task completes". 2.1.260 then removed the one-hour limit on subagents' background commands.

Still open, but not this issue's scope:

  • The "You will be notified when it completes" copy is unchanged, and an early stop still pushes a completed task notification to the parent carrying the subagent's waiting text, before the real one arrives later. A parent can still mistake that first notification for the final result. The notification's own footnote ("fires each time this agent stops with no live background children") is accurate, but only agent children count as "background children"; a live background command does not suppress the early notification.
  • In one-shot claude -p (no stream input), a top-level session's pending background command is still killed about five seconds after the result and the process exits. That is #89495.
  • Because the behaviour is behind a remote flag, it can be switched off without a release.