You spawn a subagent with the Task tool. It runs for a few minutes, prints log messages, finishes — and then the main Claude Code session either shows a totally generic summary, claims the subagent “is still running,” or skips the result and moves on.
Fastest fix: make the subagent end its turn with a real assistant text message that states the file paths it touched plus a short summary, and tell it not to dump full file contents. Add this to your Task prompt: “When done, reply with the absolute paths you created or modified and a 3-bullet summary. Do not end on a tool call. If output exceeds ~500 words, write it to a file and reply with the path.” That single change clears the large majority of relay failures.
The architecture explains why. Each subagent runs in its own isolated context window, and only its final assistant message is relayed back to the parent — intermediate tool calls, logs, and file reads stay inside the subagent and are discarded (Claude Code docs: subagents). If that final message is missing, oversized, or replaced by raw transcript noise, the parent has nothing useful to consume. The problem is almost always how the subagent ends its turn, not whether it did the work.
Which bucket are you in?
| Symptom in the main session | Most likely cause | Jump to |
|---|---|---|
| Parent shows a generic “done,” no detail | Subagent ended on a tool call, no final text | Cause 1 |
| Parent shows only a header / nothing | Final message too long, truncated on relay | Cause 2 |
| Parent says “still running” after it finished | Subagent crashed or hit a usage limit | Cause 3 |
| Parent ignores results you can see in logs | Prompt never said how to report back | Cause 4 |
| Parent knows it’s “done” but not what | Subagent wrote to disk, omitted the path | Cause 5 |
| Parent gets a huge JSON/JSONL blob | TaskOutput bug on background agents | Cause 6 |
| Result lands in transcript, next turn ignores it | Race after a Ctrl-C interrupt | Cause 7 |
Common causes
Ordered by hit rate, highest first.
1. Subagent ended with a tool call instead of a final assistant message
If the subagent’s last action is a Write, Edit, or Bash call and it never produces a closing assistant text message, the parent receives an empty or fragmentary result. Subagents must end with a real text reply.
How to judge: open the subagent transcript (see Step 1) and look at the last record. If type is tool_use or the line is a toolUseResult rather than an assistant message with a text block, that is the bug.
2. Final message exceeded the relay buffer
The subagent’s result is passed back as a single message. Very long output (roughly several thousand tokens and up) can be truncated, so the parent sees only a header or nothing.
How to judge: check the subagent’s last message length. If it is dumping entire file contents instead of summarizing, it is probably oversize.
3. Subagent crashed or hit a usage limit, no result emitted
If the subagent ran out of tokens, hit a rate limit, or threw an internal error, it can exit without ever producing a final message. The parent often interprets the missing return as “still running.”
How to judge: scan the subagent’s last few records for error, a usage-limit signal, or a max_tokens stop reason on the final assistant turn.
4. The parent’s prompt did not tell the subagent how to report back
Subagents follow the instructions they were given. If you said “do X” but never said “return the result as Y,” they may print to logs only, assuming the parent will read those — which it cannot.
How to judge: re-read the Task description you wrote. Does it say “reply with the file path and a one-paragraph summary”? If not, that is the gap.
5. Subagent wrote results to disk but never mentioned the path
Many subagents save large output to a file and finish with “done.” The parent has no idea where to read, so from its perspective the result is “done” — and useless.
How to judge: ask the parent “what file did the subagent create?” If it cannot answer, the subagent forgot to put the path in its final message.
6. Background agent returned the raw transcript instead of a summary
If you ran the subagent with run_in_background=true and pulled its result with TaskOutput, a known regression could return the entire conversation as a serialized JSONL string — 40-plus messages, hundreds of kilobytes — instead of the agent’s final text. Symptoms: the parent’s context balloons and you see lines like {"parentUuid":...,"isSidechain":true,"type":"user",...} in the result (anthropics/claude-code #20531).
How to judge: if the relayed result is JSONL with isSidechain / parentUuid fields rather than a clean summary, you hit this. It was fixed in later 2.1.x builds — run claude --version, then claude update. Until you update, use the file-based handoff in Step 4.
7. Stale parent state — the parent moved on before the subagent finished
In rare cases (especially after an interactive Ctrl-C) the parent advances its own turn before the subagent posts back. The result lands in the transcript but is never consumed by the next assistant turn.
How to judge: inspect the message ordering. If a parent-side assistant message appears between the subagent dispatch and the subagent return, you hit this race.
Before you start
- Decide whether you can re-run the subagent or whether the work is too expensive to repeat.
- Save the subagent transcript (Claude Code keeps it under
~/.claude/projects/...) before it gets overwritten. - Note the exact wording of the Task prompt; you will likely rewrite it.
- Have a small reproduction task ready for testing your fix.
Information to collect
- Claude Code version:
claude --version(latest is 2.1.x as of June 2026). - The full Task prompt you used to spawn the subagent.
- The subagent transcript JSONL from
~/.claude/projects/<project>/. - The parent’s response after the subagent supposedly finished.
- Any files the subagent created (paths and sizes).
- Token usage shown at session end, if available.
Step-by-step fix
Step 1: Locate the subagent transcript
Claude Code writes each session as a JSONL file under a directory keyed to your project path. List the most recent ones:
ls -lt ~/.claude/projects/*/*.jsonl | head -10
Subagent (sidechain) runs are stored separately, one file per spawned agent:
ls -lt ~/.claude/projects/*/*/subagents/agent-*.jsonl | head -10
Each line is one record; subagent lines carry "isSidechain": true and chain via parentUuid. To isolate just the subagent’s turns in a session file:
grep '"isSidechain":true' ~/.claude/projects/<project>/<session-id>.jsonl | tail -5
The last line tells you exactly how the subagent ended.
Step 2: Confirm the final message exists
A healthy subagent ends on an assistant record whose content holds a text block, for example:
{ "type": "assistant", "message": { "content": [ { "type": "text", "text": "Done. Wrote /tmp/report.md ..." } ] } }
If the last record is a toolUseResult (or an assistant record whose only content is tool_use) with no following assistant text, the subagent never produced a final reply. That is Cause 1.
Step 3: Rewrite the Task prompt to demand a structured reply
Instead of “investigate X,” say:
Investigate X. When done, reply with exactly:
- File paths you created or modified (absolute)
- A 3-bullet summary of findings
- Any open questions
Do not end on a tool call.
Explicit format requests reduce relay failures dramatically. If you spawn this kind of worker repeatedly, bake the contract into a reusable subagent definition under .claude/agents/ (create one with /agents) so every run inherits the same reporting rules.
Step 4: Cap the subagent output size (file-based handoff)
Tell the subagent to summarize rather than dump:
If the result is longer than ~500 words, write the full
output to a file and reply with the absolute path plus a short summary.
This avoids the relay-buffer truncation in Cause 2, and it is also the workaround for the TaskOutput background-agent bug in Cause 6: the parent reads the file directly instead of consuming a bloated task.output field.
Step 5: Re-run with a small reproduction
Spawn the same subagent with a trivial task (“list 3 files under src/”). If the relay works for the small case, the original failure was size or format. If it fails even small, suspect crashes or usage limits.
Step 6: Inspect for usage-limit or token-cap errors
Search the subagent transcript for usage_limit, max_tokens, or error:
grep -E 'usage_limit|max_tokens|"type":"error"' ~/.claude/projects/<project>/<session-id>.jsonl
If found, reduce the subagent’s scope or split the task across two subagents. See Claude Code stops mid-task usage limit for the limit side of this.
Step 7: For race conditions, rerun without Ctrl-C interrupts
Avoid pressing Ctrl-C while a subagent is in flight; let it finish on its own. If you must cancel, cancel the whole turn and start fresh rather than half-interrupting.
How to confirm it’s fixed
- The parent’s next assistant message references the subagent’s actual findings, not a generic “done.”
- Files the subagent wrote are mentioned by absolute path in the parent’s response.
- A repeat run on the same prompt produces a consistently relayed result.
- The subagent transcript’s last record is an
assistanttextmessage, not a tool call or a raw JSONL blob.
Long-term prevention
- Keep a snippet for Task prompts: “reply with a structured summary, do not end on a tool call.”
- Use file-based handoff for any subagent producing more than a few hundred lines of output.
- Set hard scope limits on subagent work: one subagent, one well-defined task.
- Periodically review transcripts to spot recurring relay failures and update your prompt template or
.claude/agents/definition. - Prefer multiple small subagents over one long-running one when the scope grows.
- Keep Claude Code current with
claude update; several relay andTaskOutputregressions were fixed across the 2.1.x line.
Common pitfalls
- Assuming the parent can “see” the subagent’s intermediate logs. It cannot; only the final message is relayed.
- Letting the subagent dump entire file contents back instead of summarizing.
- Cancelling halfway with Ctrl-C and then expecting the parent to recover gracefully.
- Forgetting the subagent runs in an isolated context, so it cannot reference parent variables or files unless you tell it.
- Spawning a subagent for a task that does not benefit from isolation; sometimes inline is better.
FAQ
- Why does my parent say the subagent is still running when it finished? The final message was missing or empty (Cause 1 or 3). The parent has no completion signal, so it waits or guesses.
- Can the parent read the subagent’s tool calls? No. Only the closing assistant text message is relayed; intermediate tool calls and results are discarded.
- What is the size limit on relayed messages? There is no published hard number, but practically you want the final message in the low thousands of tokens. Beyond that, summarize or write to disk and return the path.
- Why did I get a giant JSONL blob instead of a summary? That is the
TaskOutputbackground-agent regression (Cause 6). Update Claude Code withclaude update, and meanwhile have the agent write to a file and return the path. - Does this affect plugin-defined and custom agents too? Yes. Any subagent spawned via the Task tool, including built-ins like Explore and Plan and anything in
.claude/agents/, follows the same final-message relay contract. - Where is the subagent transcript saved? Under
~/.claude/projects/<project>/, as.jsonl. Subagent runs live in<session-id>/subagents/agent-*.jsonl; subagent lines are tagged"isSidechain": true.