2.1.198: headless -p run exits ~50s after model launches Agent subagent with run_in_background=true; bg wait ceiling not applied (regression from 2.1.197)

Status Open
Reported on v2.1.198
Maintainer reply None cached
Activity 3 comments · opened Jul 2, 2026

Summary

In Claude Code 2.1.198, a headless run (claude -p style, driven by anthropics/claude-code-action@v1 via the Agent SDK) exits roughly 50 seconds after the model launches an Agent-tool subagent with run_in_background: true. The backgrounded subagent is abandoned mid-work, no result message is emitted on the stream, and the run produces no output. The documented background-subagent wait ceiling for print mode (CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS, default 10 minutes) does not kick in.

The same workflow on 2.1.197 (previous day, same prompt) completed normally. The failure is intermittent because it depends on a model choice: on 2.1.198, runs where the model launches subagents as blocking calls still succeed; runs where it picks run_in_background: true die.

Environment

  • Claude Code 2.1.198 (installed by anthropics/claude-code-action@v1, commit 6c0083bb7289c31716797a039b6367b3079cc46e, which bumps CLI 2.1.197 -> 2.1.198 and Agent SDK 0.3.197 -> 0.3.198)
  • Vertex AI provider (CLAUDE_CODE_USE_VERTEX=1), model claude-opus-4-7
  • Linux CI runner (Ubuntu 24.04), non-interactive
  • Prompt: a multi-agent PR review orchestrator that instructs the model to "launch 5 Task sub-agents in parallel", collect their JSON findings, then post a review

What happened (from the stream JSON in the CI log)

  1. 13:37:58 - orchestrator calls the Agent tool with "run_in_background": true for the first review subagent. Tool result comes back immediately:
"tool_use_result": {
  "isAsync": true,
  "status": "async_launched",
  "agentId": "a2d3cf5fb9e237fa1",
  "resolvedModel": "claude-opus-4-7",
  "outputFile": "/tmp/claude-1001/.../tasks/a2d3cf5fb9e237fa1.output",
  "canReadOutputFile": true
}
  1. 13:38:00-13:38:01 - the backgrounded subagent starts working (task_started, task_progress, first Read tool call and its tool_result appear on the stream).
  2. The stream then truncates mid tool_result message. No further assistant turns, no "type": "result" message at all.
  3. 13:38:51 - the action step completes with exit code 0 and the job moves on. Total elapsed since the async launch: ~53 seconds. None of the orchestrator's later phases ran and nothing was written or posted.

For comparison, a run minutes later on the same 2.1.198 install, where the model happened to launch its subagents as blocking calls (no async_launched anywhere in the stream), completed all phases and ended with a normal "subtype": "success" result message. The last 2.1.197 run the previous day also completed normally.

Expected

Per the headless docs, print mode should wait for background subagents up to CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS (default 10 minutes), and the orchestrator should be able to collect the subagent's result and continue. Instead the process exits within about a minute, silently, with exit code 0.

Impact

Any headless orchestration prompt that fans out subagents now fails nondeterministically, depending on whether the model elects to background them. Exit code 0 makes the failure silent in CI.

Workarounds we applied

  • CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 in the action env, which removes run_in_background from the Agent tool.
  • A prompt instruction to never pass run_in_background.
  • Known-good fallback: pinning the action to the last commit shipping 2.1.197 (fad22eb3fa582b7357fc0ea48af6645851b884fd).

Happy to provide fuller (lightly redacted) stream logs from the failing run if useful.

View original on GitHub ↗

3 Comments

MuffinSchwartzMD · 1 month ago

Corroborating data from 2.1.198 on macOS (Darwin 25.6.0):

In a 16-child harness (claude -p --output-format json, repo cwd, --session-id set, each child dispatching 5–6 parallel Task/Agent subagents), stdout emitted no result envelope in 13/13 dispatching runs — exit 0, stderr empty, transcript JSONL complete, file side-effects all landed. 0-dispatch children under the identical invocation emitted well-formed {"type":"result",...} envelopes every time.

A minimal repro with one synchronous Task dispatch from an empty cwd did not reproduce (well-formed envelope, num_turns=2), which is consistent with the async/background-dispatch trigger described here rather than a flat dispatched-vs-not correlation. Distinguishing datum from our side: our failing children dispatched multiple subagents in parallel; we did not confirm from transcripts whether the harness sessions' Agent calls carried run_in_background: true — if useful, we can check the recorded JSONLs and report back.

Repro harness and per-run records available on request.

doublefx · 1 month ago

Additional data points from a different launch path — an SDK streaming-input session (not one-shot -p), Claude Code 2.1.201, Linux/WSL2, first-party API (no Vertex):

  • Since 2.1.198 the Workflow tool also backgrounds unconditionally in headless SDK sessions (the launch tool_result returns "Workflow launched in background" immediately). In my setup the driver session launches one Workflow run and waits for its completion notification.
  • Unlike the ~50s exit reported here, in the streaming-input case the ceiling IS applied — the pending background Workflow child is killed at ~602s, matching the documented CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS default of 10 min.
  • Causality confirmed: forcing CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS=60000 on the spawning process kills the same child at 62s (the same workflow completes normally when it fits under the ceiling). Raising the ceiling is the workaround I've wired in.
  • For completeness: neither CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 nor CLAUDE_CODE_ENABLE_TASKS=false prevents the Workflow tool from backgrounding (both verified present in the child's /proc/<pid>/environ), so there is currently no opt-out back to blocking launches in headless.

Related: #64627 reports the same silent kill (~13 min there) with no completion/death signal to the parent — that matches what I see: the parent only discovers the death by polling disk/process state; no result/failure message is emitted for the abandoned child.

A headless opt-out (Workflow/Agent tool runs blocking again), or at minimum a death signal on ceiling expiry, would make orchestrating long multi-agent workflows via the SDK reliable.

neefrehman · 26 days ago

Still present on 2.1.220, on Bedrock rather than Vertex (so this looks provider-independent), also driven by anthropics/claude-code-action on a Linux runner. Two datapoints that might help, since our failure looks like a variant of this rather than an exact match.

1. A second failure mode: clean end_turn rather than truncation

Our stream doesn't truncate — we get a well-formed terminal envelope:

{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "terminal_reason": "completed",
  "num_turns": 28,
  "permission_denials": [],
  "result": "Good progress — 7 of 20 agents have completed so far. Waiting for the remaining 13 agents to finish their verification."
}

So instead of the harness killing the process mid-tool_result, the model voluntarily ends its turn with a progress report about subagents that are still running. 13 subagents are abandoned (the last verdict file was written 2s before exit), exit code is 0, the job is green, and none of the downstream phases — which were supposed to apply edits and open a PR — ran. Same net impact as the original report, but reached by a different path, so it may not be fixed by whatever fixes the truncation.

I suspect an interaction with this 2.1.211 entry:

Improved background agent result reporting — Claude now reports the status of still-running agents and waits for the real completion instead of fabricating results

That's right interactively. Under -p there's no user and no subsequent turn, so "reports the status of still-running agents" is the final answer, and the "waits for the real completion" half can never happen.

2. run_in_background is no longer model-elected, so the prompt workaround has nothing to act on

Same prompt, three versions, from our CI logs:

| Claude Code | run_in_background in Agent inputs | async_launched | outcome |
|---|---|---|---|
| 2.1.131 | 22 | 22 | completed, PR opened |
| 2.1.195 | 0 | 0 | completed, PR opened |
| 2.1.220 | 0 | 20 | abandoned as above |

On 2.1.220 the model never passes run_in_background at all, yet every spawn returns "status": "async_launched" / "isAsync": true. Backgrounding is now implicit, so "instruct the model never to pass run_in_background" no longer has anything to target.

Worth noting that 2.1.131 backgrounded 22 subagents explicitly and still completed normally — consistent with the 2.1.198 note that this was "previously a gradual rollout", and suggesting the regression isn't backgrounding itself but how the parent turn terminates around it.

CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS also doesn't appear to apply: exit came ~70s after the final spawn, well inside the 10-minute default.

We're trying CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 next and will report back whether it holds on 2.1.220, given #69030 reports it being ignored for multi-subagent spawns.

---

Update: CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 does work on 2.1.220

Confirmed on the same 2.1.220 install that produced the failure above — same prompt, same runner, only the env var added:

| | before | with CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1 |
|---|---|---|
| Agent tool calls | 20 | 20 |
| async_launched / isAsync | 20 | 0 |
| num_turns | 28 | 85 |
| duration_ms (Claude step) | 188,598 | 518,967 |
| outcome | 13 subagents abandoned, no PR, exit 0 | all 20 verdicts collected, PR opened |

All 20 spawns ran synchronously and the orchestration completed end to end.

This doesn't contradict #69030, I think — that report is specifically about fork subagents (CLAUDE_CODE_FORK_SUBAGENT), which it says background regardless of the variable. Ours are named general-purpose subagents spawned through the Agent tool, and for those the variable is honoured on 2.1.220. So the opt-out looks effective for Agent-tool spawns but not fork spawns.

It's a workaround rather than a fix, though: it serialises the fan-out entirely (~2.8x wall clock here) so headless orchestration currently has to choose between parallel subagents and completing at all.