[BUG] Usage-limit-terminated subagent is recorded as status: completed and rendered as "Done"

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Jul 31, 2026

Preflight Checklist

  • [x] Searched existing issues (#78231 covers the discarded-output/billing angle; #73990 is the docs gap). Neither covers the success-status misreport below.
  • [x] Single bug report
  • [x] Latest version (2.1.220)

What's Wrong?

When a session usage limit terminates a foreground subagent, the Agent tool returns a successful tool result. The transcript records toolUseResult.status: "completed" and omits is_error, so the UI renders the killed agent as "Done" alongside its tool-use and token counts.

This contradicts the documented behavior in sub-agents.md:

Foreground: if a rate limit, overload, or server error cuts off a subagent that already produced output, the Agent tool returns that partial output with a note that the subagent was cut off and didn't finish its task. Otherwise the tool call fails with Agent terminated early due to an API error.

Observed: the tool call does not fail. It takes the "already produced output" path and succeeds — even though the only recovered output is the agent's preamble sentence, emitted before it made a single tool call.

Concrete case (3 independent occurrences, transcripts on disk):

| Session | Agent | Tool uses | Tokens | Recovered payload |
|---|---|---|---|---|
| A | Explore | 25 | 67,570 | "I'll explore these components in parallel." (42 chars) |
| B | Explore | 25 | 74,102 | preamble sentence only |
| B | Explore | 30 | 68,910 | preamble sentence only |

Raw structure of the returned tool result (session A):

{
  "toolUseResult": {
    "status": "completed",
    "agentType": "Explore",
    "totalTokens": 67570,
    "totalToolUseCount": 25,
    "toolStats": { "bashCount": 25 },
    "content": [
      { "type": "text", "text": "Agent terminated early due to an API error: You've hit your session limit · resets 4:20pm (Asia/Taipei)\n\nEverything below is PARTIAL output recovered from the agent before it was cut off. The agent did NOT finish its task — treat these results as incomplete." },
      { "type": "text", "text": "I'll explore these components in parallel." }
    ]
  }
}

Note the is_error field is absent from the tool_result content block entirely.

Two user-visible consequences:

  1. The UI lies. The screen showed 2 Explore agents finished · Done · Done while one of those agents had been killed 60 seconds earlier with zero work product. A user watching the terminal has no way to tell a completed agent from a killed one.
  1. The "PARTIAL output recovered" preamble is self-contradictory in this state. It promises recovered partial output, then attaches a sentence the agent emitted before doing any work. 25 tool calls of retrieval produced nothing recoverable. If nothing meaningful can be recovered, the "partial output" framing actively misleads both the model and the user into thinking some findings survived.

What Should Happen?

  1. A subagent terminated by a usage limit should not be recorded as status: "completed", and the UI should not label it Done. Mark it failed/terminated so it is visually distinguishable.
  2. The "partial output recovered" path should only be taken when there is actual post-tool-call output to recover. Preamble text emitted before the first tool call is not partial findings — in that case the tool call should fail cleanly, as the docs already specify.

Steps to Reproduce

Preconditions: an account close enough to a session limit that it will be crossed during a fan-out.

  1. In Claude Code CLI, dispatch 2+ foreground Explore subagents in a single Agent tool block on a task requiring substantial exploration.
  2. Allow the session limit to be crossed while the agents are in flight.
  3. Observe the terminal: killed agents render as Done with their tool-use and token counts intact.
  4. Inspect the session transcript at ~/.claude/projects/<project>/<session>.jsonl — the corresponding entry has toolUseResult.status: "completed" and no is_error, with a content array whose second block is the agent's preamble.

Claude Model

Opus (claude-opus-5)

Is this a regression?

I don't know — the surrounding behavior changed in v2.1.199/v2.1.200 (per the changelog and #73990), so this may be an edge case introduced by that fix.

Claude Code Version

2.1.220 (Claude Code)

Platform

Anthropic API (Claude Pro subscription, session limit)

Operating System

macOS

Terminal/Shell

Other (tmux + fish)

Additional Information

Related: #78231 reports the same underlying discard-on-termination behavior from Cowork with a monthly spend limit, focused on the billing impact. #73990 documents that the "no output at all" path is under-specified. This report is specifically about the success status and Done label on a terminated agent, which neither covers.

The practical risk this creates: in a fan-out where some agents survive and some are killed, the surviving agents' reports look comprehensive, and the killed ones are labeled Done. Nothing in the UI signals that the exploration was incomplete before the parent proceeds to act on it.

View original on GitHub ↗