[BUG] Headless session exits "success" seconds after dispatching subagents — work orphaned, no error surfaced

Status Open
Reported on v2.1.218
Maintainer reply None cached
Activity 0 comments · opened Aug 8, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

In a headless SDK session (anthropics/claude-code-action@v1 running the code-review@claude-code-plugins plugin), the model dispatched a multi-agent review fan-out, announced it, and the session terminated 6–15 seconds later with "subtype": "success", "is_error": false. No review was ever produced. The GitHub Action reported success, so nothing signalled that the review had silently not happened — the PR comment simply stayed frozen at an unfinished todo list.

This is the headless counterpart of #81254 (interactive: Claude presents a plan before backgrounded subagents finish). Interactively the session survives, so the user can notice and the subagent result still lands. Headless, ending the turn ends the process — the pending work is killed and the exit code says everything is fine.

Relevant context: 2.1.218 changed /code-review to run as a background subagent (see #80385). Our workflow invokes the plugin's namespaced /code-review:code-review; I haven't verified whether the plugin command shares the built-in's backgrounding, so the mechanism below is the best-supported explanation rather than a proven one.

Two runs on 2026-08-08, same repo, unrelated PRs. Timestamps from the run logs plus the GitHub comment edit history (userContentEdits), which is the only way to see the model's actions since the transcript is hidden in CI:

Run A (PR "350"):

| Time (UTC) | Event |
|---|---|
| 14:27:08 | SDK init |
| 14:27:17 | model's first tracking-comment update (todo list) |
| 14:33:35 | model updates comment: "7 review agents dispatched, in progress" |
| 14:33:50 | result: success, duration_ms: 401422, num_turns: 12, total_cost_usd: 5.2874, permission_denials_count: 50 |

Run B (PR "355"):

| Time (UTC) | Event |
|---|---|
| 15:18:53 | model's first tracking-comment update |
| 15:23:42 | model updates comment: "multi-agent review running" |
| 15:23:48 | result: success, duration_ms: 303082, num_turns: 13, total_cost_usd: 5.2872, permission_denials_count: 23 |

In both cases the session died 6–15 s after the dispatch edit — far too fast for any review agent to have finished, and the model's own todo list still had "Post review feedback" unchecked. Plain reruns of both (same CLI version, same plugin) reviewed in the main conversation instead of fanning out and completed normally, which is why this reads as nondeterministic agent behaviour that the harness should be catching.

Two details that may help narrow the mechanism:

  1. Near-identical total cost across unrelated PRs: $5.2874 vs $5.2872 (0.003% apart), despite different durations, turn counts and diffs. That looks less like coincidence and more like both runs hitting the same hard ceiling (context window or an internal budget) during the pre-dispatch phase — which may be why the model pivoted to a subagent fan-out immediately before dying. If such a ceiling can end a query while work is pending, that's worth checking independently of the background-subagent path.
  2. Permission-denial storms (50 and 23): the action's prompt instructs the model to run git diff origin/dev...HEAD, but the default allowlist has no read-only git commands, so on large PRs it grinds through the changeset with Read() instead. Healthy runs on the same repo logged 0–3 denials and cost $0.36–2.85. The denials are invisible without diffing the result JSON.

I can't distinguish with certainty between (a) background-dispatched subagents orphaned at turn end, (b) subagent dispatch denied and the model giving up with a hidden final message, or (c) a context/budget ceiling ending the loop right after dispatch — the hidden transcript caps what's diagnosable from outside. All three share the same fix surface: a headless query should not exit success with work pending or with the task demonstrably unfinished.

What Should Happen?

In headless/--print/SDK mode, one of:

  1. Await pending background subagents and re-invoke the model with their results before emitting result (the interactive re-invocation behaviour, applied headlessly); or
  2. Refuse background dispatch in headless mode so the model receives an error it can react to and falls back to reviewing inline.

Either is fine. Silently exiting with success while killing pending work is the worst of the options, because it converts a recoverable failure into an invisible one. Compare #74685, where ScheduleWakeup is a silent no-op in --print mode — same shape of problem: a mechanism that assumes a future re-invocation which headless never delivers.

Secondary asks:

  • Surface permission_denials_count above some threshold as a log warning naming the denied tools. 50 hidden denials in one run made this substantially harder to diagnose.
  • (For claude-code-action, if it's the better home: with track_progress: true, a run ending while the tracking comment still shows unchecked todos and an in-progress spinner could be a step failure or at least a warning annotation. Both of our stalls were caught only by a human noticing the frozen comment.)

Error Messages/Logs

# Run A, final SDK result — emitted 15 seconds after the model announced
# "7 review agents dispatched, in progress". No error anywhere in the log.
{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "duration_ms": 401422,
  "num_turns": 12,
  "total_cost_usd": 5.287379350000001,
  "permission_denials_count": 50
}

# Run B, same shape:
{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "duration_ms": 303082,
  "num_turns": 13,
  "total_cost_usd": 5.287212800000002,
  "permission_denials_count": 23
}

Steps to Reproduce

Reproduces intermittently — 2 of ~5 review runs on the same day. It needs a diff large enough to push the model toward a subagent fan-out.

  1. Configure a GitHub Actions workflow using anthropics/claude-code-action@v1:

```yaml

  • uses: anthropics/claude-code-action@v1

with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
track_progress: true
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
prompt: '/code-review:code-review'
```

  1. Open a large PR (ours were 59 files / +611 −1874, and 29 files) so the model has a lot of diff to process. Do not add read-only git tools to the allowlist, so it burns context via Read().
  2. Watch the tracking comment. When the model writes "N review agents dispatched, in progress", check the run result.
  3. Failure case: the session emits result: success seconds later, no findings are posted, and the workflow goes green with the comment frozen mid-todo.

Because the transcript is hidden by default in the action, userContentEdits on the bot comment (GraphQL) is the most reliable way to timestamp what the model actually did:

query { repository(owner:"OWNER", name:"REPO") { issueOrPullRequest(number: N) {
  ... on PullRequest { comments(first: 5) { nodes { databaseId
    userContentEdits(first: 20) { nodes { editedAt diff } } } } } } } }

Claude Model

claude-sonnet-5

Is this a regression?

Yes, likely — /code-review moved to a background subagent in 2.1.218 (#80385). We have no confirmed pre-2.1.218 baseline for this workflow, so I can't state the last good version with confidence.

Last Working Version

Unknown; suspect pre-2.1.218.

Claude Code Version

2.1.226 (native build), @anthropic-ai/claude-agent-sdk@0.3.226

Platform

GitHub Actions (anthropics/claude-code-action@v1), headless SDK

Operating System

ubuntu-latest (GitHub-hosted runner)

Terminal/Shell

bash (GitHub Actions runner, non-interactive)

Additional Information

Related: #81254 (interactive analog — plan presented before backgrounded subagents finish), #80385 (docs: /code-review backgrounding in 2.1.218), #74685 (ScheduleWakeup silent no-op in headless).

Workaround we've deployed, in case it helps others hitting this: append constraints to the prompt telling the model not to end its turn while subagents are running or todos are unchecked, to run review subagents in the foreground, and to fall back to reviewing inline if dispatch fails; plus allowlisting read-only git diff/log/blame/show and gh pr diff/view so it stops burning context on Read(). That's a prompt-level patch over a harness-level problem, which is why I'm filing this.

View original on GitHub ↗