[MODEL] Forked subagents (subagent_type: fork) sometimes report status: completed after doing no work, with a result message unrelated to the assigned task
Report Unexpected Model Behavior
Type of Behavior Issue: Subagent behaved unexpectedly
Two related failure modes observed in the same batch of parallel subagent_type: "fork" dispatches:
- Silent non-completion reported as
status: completed. Two of four forked subagents made zero edits to any of their assigned files, yet their task-notification reportedstatus: completed. One'sresultfield had no connection to the assigned task at all; the other's named one real filename from its own assignment but described the work as still pending, third-person, rather than reporting the (nonexistent) outcome. resulttext that doesn't reflect what the agent actually did, independent of (1). One of the two agents that did complete its assigned work correctly (verified independently) also returned aresultsummary that was generic and unrelated to the real work performed.
Only 1 of the 4 forks in this batch produced both correct work and an accurate, specific completion report.
What You Asked Claude to Do
Using the Agent tool with subagent_type: "fork", I dispatched 4 agents in a single turn so they'd run in parallel, each continuing the current conversation's full context (per the fork tool's own documented semantics: "forks yourself... inherits your full conversation context"). Each fork was given a distinct, detailed prompt: a fixed list of 4-5 specific Swift source files to edit (fixing swift-format lint violations in doc comments), explicit instructions not to touch any file outside its own list, not to touch git, and to verify its own work with the linter before reporting back per-file results. (Task: a code-style remediation sweep across ~19 files total, split into 4 disjoint groups of 4-5 files each.)
What Claude Actually Did
- Fork "D" (assigned 5 specific files) returned
status: completedafter ~43 seconds and only 2 tool calls. Itsresultfield read, in full:
> "That tool is specific to /loop mode, not applicable here, I'll just wait for the natural task-completion notifications from the four background agents."
This has no relationship to the assigned file-editing task at all. Independently re-running the exact lint command against all 5 of its assigned files afterward showed every single originally-flagged violation still present, byte-for-byte, and git status/git diff showed no changes to any of them. The agent did not open, read, or edit any of its assigned files.
Notably, this text closely mirrors reasoning the parent session was independently going through at roughly the same wall-clock point: the parent had just tried (and failed, due to a parameter-validation error) to use the ScheduleWakeup and Monitor tools to wait on the very same set of background fork agents, and had concluded (in its own, separate turn) that it should just let the natural task-completion notifications arrive instead. It reads as though this fork's final message was generated from, or contaminated by, the parent's own unrelated tool-use reasoning happening concurrently in the background, rather than from the fork's own assigned prompt.
- Fork "A" (assigned 4 different specific files) returned
status: completedafter 614.7 seconds (~10 minutes) and 23 tool calls, consuming 460,095 subagent tokens, roughly the same order of magnitude of time/tokens/tool-calls as its two sibling forks that did complete their assigned work correctly (119.6s/21 calls/432,813 tokens, and 355.0s/38 calls/453,897 tokens respectively). So this instance was not idle or fast-failing: it spent a realistic amount of effort on something, just not on the assigned task. Itsresultfield read, in full:
> "Still waiting on the last fork (Group A) to finish DeviationTools.swift, the largest of the sweep files."
This is a second, distinct symptom: the agent refers to itself in the third person ("the last fork (Group A)") and describes itself as still pending/in-progress, as if it were the orchestrating parent narrating the state of a child agent, rather than a child agent reporting on its own completed execution. Again, independently re-running the linter against all 4 of its assigned files afterward showed zero changes; every originally-flagged violation was still present.
- Fork "C" (assigned 5 further specific files) did correctly complete 100% of its assigned edits, independently verified file-by-file with the linter after the fact. However, its own
resultfield was equally uninformative and unrelated to the real work performed:
> "I'll pause here and wait for the background agents to report back before continuing the sweep."
This shows the "result text doesn't reflect the agent's actual actions" problem is broader than just the silent-no-op case in Forks A/D above; it also occurs on a run where the underlying task genuinely was completed correctly.
- Fork "B" (assigned the remaining 5 files) both completed its work correctly and returned an accurate, specific, per-file
resultsummary that matched the real diff. This is the only one of the four that behaved as expected end to end.
I only caught (1) because I habitually re-verify a subagent's claimed work against the actual artifact (re-running the same linter the agent was told to use) rather than trusting the natural-language completion report. Had I trusted the status: completed + plausible-sounding result text at face value, roughly half the sweep would have silently gone unfixed while being reported, and later committed/PR'd, as done.
Expected Behavior
- A
status: completedtask-notification should be a trustworthy signal that the agent's final turn actually reflects, and ideally was actually produced from, its own assigned prompt, not an unrelated or self-referential message that appears to leak in from concurrent parent-side (or sibling-fork) activity. - If a forked agent cannot or does not complete its assigned task for any reason, its final report should say so honestly (e.g., "I did not complete the assigned edits because...") rather than emitting text that reads as though it belongs to a different conversational context entirely.
- The
resultfield, when work genuinely was completed, should reliably summarize that work, not a generic placeholder unrelated to it.
Files Affected
Not applicable in the "Claude touched files it shouldn't have" sense; this is the inverse: files that should have been edited (per the fork's own explicit, itemized assignment) were not touched at all, while the tool nonetheless reported success.
Permission Mode
Not sure / not directly observable from the transcript. File edits were clearly permitted in this session in general (the sibling forks that did complete their work wrote to disk without any visible permission-prompt friction), so this doesn't look like a permission denial silently masquerading as success.
Can You Reproduce This?
Sometimes (intermittent). This is a single real occurrence encountered during normal use (2 of 4 concurrently-dispatched forks affected), not a constructed minimal repro; I have not yet attempted to isolate a minimal standalone reproduction outside of this task. Flagging the pattern (concurrent fork dispatch + concurrent parent-side tool activity, including a parent-side tool-call error occurring at roughly the same time) as a plausible triggering condition, correlation only, not confirmed causation.
Steps to Reproduce
- In a single Claude Code turn, dispatch several (I used 4)
Agenttool calls withsubagent_type: "fork", each with a distinct, detailed, file-scoped editing task and an explicit "report back per-file" instruction, so they execute concurrently in the background. - While they are still running, continue driving the parent conversation with further tool calls of your own (in my case: unrelated verification work, plus at one point calling
ScheduleWakeupand thenMonitorwith invalid/inapplicable parameters, both of which errored). - Wait for each fork's
<task-notification>to arrive withstatus: completed. - Independently re-verify each fork's claimed work against the actual filesystem/artifact state (do not trust the
resulttext). In my case, ~50% of the dispatched forks had done nothing to their assigned files despite reporting success, and a further fork's success report was accurate in status but empty of any real content.
Claude Model
Sonnet (dropdown only offers Sonnet/Opus/Haiku granularity), exact model id claude-sonnet-5 ("Sonnet 5") for both the parent session and, per the fork tool's documented semantics ("forks... always run on your model"), the dispatched forks.
Relevant Conversation
Verbatim result fields quoted above. Resource-usage figures from each fork's <usage> block, for comparison:
| Fork | Outcome | tool_uses | duration_ms | subagent_tokens |
|---|---|---|---|---|
| A | 0/4 files edited, but reported completed | 23 | 614,729 | 460,095 |
| B | 5/5 files edited correctly, accurate report | 21 | 119,623 | 432,813 |
| C | 5/5 files edited correctly, but generic/unrelated report | 38 | 355,010 | 453,897 |
| D | 0/5 files edited, but reported completed | 2 | 42,558 | 423,575 |
Impact
Medium (direct cost this time: I had to manually redo the two forks' work by hand after independently discovering it hadn't happened). Flagging that the latent risk is higher than "Medium" for any caller who doesn't independently re-verify a subagent's claimed output against the real artifact: the natural-language completion report was fully plausible-sounding and gave no indication anything was wrong, which is exactly the condition under which a user or orchestrating agent would reasonably trust it and move on (in this case, that would have meant committing, opening a PR, and shipping a public release claiming full remediation while roughly half the intended files were silently untouched).
Claude Code Version
2.1.235 (Claude Code)
Platform
Anthropic API (default; not explicitly confirmed from within the session, but no Bedrock/Vertex-specific configuration was in use)
Operating System
macOS (26.6.1 / Darwin 25.6.0, arm64)
Additional Context
- Orchestrating multiple subagents in parallel (via
subagent_type: "fork"or otherwise) is a documented, first-class Claude Code workflow, and its usefulness depends entirely on the completion report being trustworthy enough not to require independently re-verifying every dispatched agent's actual output by hand, which mostly defeats the point of delegating in the first place. - I don't have access to (and was specifically instructed not to read, to avoid context overflow) the full JSONL transcripts of the affected forks' own internal turns, so I can't say from direct inspection whether they never actually issued the Read/Edit/Bash tool calls their prompts called for, or issued them and then discarded the results before their final message. The 23-tool-call, ~10-minute, 460K-token case (Fork A) in particular suggests the agent was doing something substantive internally; it just wasn't the assigned task, and/or its final response didn't reflect whatever it had actually done.
- I'm deliberately not naming the specific downstream project/repository this occurred in (unrelated private/business context, not needed to reproduce a Claude Code platform issue).