[Bug] Subagents silently terminate long-running background shells on turn end; SendMessage resumption auto-converts to async without warning
Bug Description
Title: Claude Code subagents silently drop long-running background shells on turn end; SendMessage resumption can be auto-async'd without warning
Summary
When a subagent uses run_in_background: true to launch a long-running Bash command (in my case codex exec -p quality, which routinely takes 5–15 minutes), the subagent sometimes ends its turn before the shell exits. The shell dies with the subagent session, discarding whatever the background process was doing — including already-billed external API tokens.
Concrete repro
My subagent prompt contained explicit, multi-paragraph constraints: "You MUST loop BashOutput until shell exits. Do NOT return an interim message. Do NOT hand off to a future turn." Despite this:
1. First attempt: subagent returned "Codex is still searching. Let me wait for Monitor to signal completion" and ended its turn. Background shell died. ~100K Codex tokens lost.
2. Second attempt (SendMessage to resume + a fresh Agent dispatch): the new Agent call returned "Async agent launched successfully" — the harness itself promoted it to an async background agent. My prompt constraints became irrelevant because the agent was no longer synchronous. I had no visible signal this was going to happen, and no parameter to override it.
Why it matters
External-CLI workflows (Codex, long builds, test suites) are a core use case where background shells are the only way around Bash's 10-minute ceiling. Each failed attempt is a real external-token/compute loss. Users cannot opt out today — Agent has no synchronous: true flag.
Suggested fixes (in order of impact)
1. Add synchronous: true to the Agent tool so callers can reject auto-async classification. The auto-async decision on SendMessage resumption was invisible and surprising.
2. Surface a harness-level warning when a subagent tries to return while it still owns live background shells. Don't silently let the shell die.
3. Document the auto-async trigger conditions for SendMessage-resumed agents. Right now the behavior is hidden and user prompts can't compensate for it.
4. Consider whether Stop-hook-style enforcement on "background shells still alive" is tractable — similar to how PreToolUse hooks already gate tool calls.
This is otherwise a tool I rely on heavily for long-running reviews. Smoothing this out would meaningfully reduce wasted external-API spend for CLI-integration workflows.
Environment Info
- Platform: darwin
- Terminal: Apple_Terminal
- Version: 2.1.114
- Feedback ID: 91e44bbd-b9ae-43fa-b743-7c0d305a2a9b
Errors
[{"error":"Error: NON-FATAL: Lock acquisition failed for /Users/wordsing/.local/share/claude/versions/2.1.114 (expected in multi-process scenarios)\n at sd_ (/$bunfs/root/src/entrypoints/cli.js:2751:2177)\n at WH8 (/$bunfs/root/src/entrypoints/cli.js:2751:1257)\n at processTicksAndRejections (native:7:39)","timestamp":"2026-04-18T23:33:36.904Z"},{"error":"Error: ensureToolResultPairing: repaired missing tool_result blocks (37 -> 37 messages). Message structure: [0] user; [1] assistant(id=msg_01FtBQN5aAMFjq1GV5S2imkM, tool_uses=[]); [2] user; [3] assistant(id=msg_01KaUrxrKy9Y37ZBg4YY26Qo, tool_uses=[]); [4] user; [5] assistant(id=msg_01KMGfYBFPuhstJ6kFEMRe11, tool_uses=[toolu_017toeS2tN1Qce7LJAtmvUaY]); [6] user(tool_results=[toolu_017toeS2tN1Qce7LJAtmvUaY]); [7] assistant(id=msg_01D4J3ndcnGayMioEnGVDUm1, tool_uses=[toolu_0127JKDxhytFpSAZBb8MGfJi]); [8] user(tool_results=[toolu_0127JKDxhytFpSAZBb8MGfJi]); [9] assistant(id=msg_018qqsWiVPRgonNxnGk4tK1o, tool_uses=[toolu_017ELupVJpQtdjBqtV1HfCBN]); [10] user(tool_results=[toolu_017ELupVJpQtdjBqtV1HfCBN]); [11] assistant(id=msg_01HEMLfQkc3pzbmTgRmpi5ZG, tool_uses=[toolu_01LqEGFjXvTmPKVZLT7oJCSe,toolu_016mLHuC4myAuCyJEPjebCaa,toolu_01FrUdtS393g3VcZbk12iWuE]); [12] user(tool_results=[toolu_016mLHuC4myAuCyJEPjebCaa,toolu_01LqEGFjXvTmPKVZLT7oJCSe,toolu_01FrUdtS393g3VcZbk12iWuE]); [13] assistant(id=msg_01XGLrNRBUTXryiVd55PHFVK, tool_uses=[toolu_01LLBDirJTUzb9fDfBbMLw5u,toolu_019ZWTqLk6LykfZBvy9uUpvr]); [14] user(tool_results=[toolu_01LLBDirJTUzb9fDfBbMLw5u,toolu_019ZWTqLk6LykfZBvy9uUpvr]); [15] assistant(id=msg_013yXkKRZPbezRkSaSNWkcaa, tool_uses=[]); [16] user; [17] assistant(id=msg_01WG2DZ4bvwRCWPGLxWNSbD1, tool_uses=[]); [18] user; [19] assistant(id=msg_012GwS8M8P3tDEZ5Pr4eYidp, tool_uses=[toolu_014n9kNzCbWUph2JZmkt2Mht]); [20] user(tool_results=[toolu_014n9kNzCbWUph2JZmkt2Mht]); [21] assistant(id=msg_01T5vpikBEtoU8zoVN28RCZQ, tool_uses=[toolu_019fCRa64i1Bxg6ENvbuRkU8]); [22] user(tool_results=[toolu_019fCRa64i1Bxg6ENvbuRkU8]); [23] assistant(id=msg_01Qyn1XCGvJG299oVYwEQzLy, tool_uses=[toolu_01B3WwG1ukJateC7gCUF2inr]); [24] user(tool_results=[toolu_01B3WwG1ukJateC7gCUF2inr]); [25] assistant(id=msg_01EFwwpa7i69oZT6wXdh1aiS, tool_uses=[toolu_01M6tQUXEB87wN7FHvKpCqLw]); [26] user(tool_results=[toolu_01M6tQUXEB87wN7FHvKpCqLw]); [27] assistant(id=msg_01JzSXnN1TnkUL2AoGPwF7nL, tool_uses=[toolu_015PUQyJstD5VeWUtPZVWRR6]); [28] user(tool_results=[toolu_015PUQyJstD5VeWUtPZVWRR6]); [29] assistant(id=msg_016eXJCEWHcZbcHsQfAHvzGL, tool_uses=[toolu_017yRqmVtLfTTv1Jnm2DwHD2]); [30] user(tool_results=[toolu_017yRqmVtLfTTv1Jnm2DwHD2]); [31] assis…
Note: Content was truncated.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗