Are you TokenMaxxing hard enough? Find out in less than a minute →

Why Subagent Token Counts Are Wrong (and How to Fix Them)

Why Subagent Token Counts Are Wrong (and How to Fix Them)

You kicked off a fan-out run: ten Claude Code agents, each on its own slice of a refactor, all going at once. One of them ate the window. Your five-hour quota went from comfortable to exhausted, and you want to know which agent did it before you launch the next batch. So you run a usage tool, look at the per-agent breakdown, and the numbers make no sense. One subagent shows more tokens than the entire parent session. Another shows a count larger than the model’s context window. The totals for the account look right. The moment you drill into a single subagent, the attribution falls apart.

That is not your accounting being sloppy. It is a structural miscount, and it is worth understanding before you trust any per-subagent number.

What a subagent looks like on disk

Claude Code writes every session to a JSONL transcript under ~/.claude/projects/. One line per event: user turns, assistant turns, tool calls, tool results. Each assistant turn carries a usage block with input_tokens, output_tokens, and the cache fields (cache_read_input_tokens, cache_creation_input_tokens).

When you spawn a subagent with the Task tool, its turns land in that same transcript, tagged as a sidechain (isSidechain: true). Here is the part that trips everyone up. A subagent does not start from an empty context. It inherits the parent’s system prompt, the tool definitions, the relevant slice of the conversation so far. Then it runs its own loop on top of that inherited base.

So the subagent’s first API call already reports a large input_tokens value, most of which is context it did not generate. It was handed that context. Every subsequent turn inside the subagent resends the accumulated context again, because that is how a stateless chat completion works: the full prompt goes over the wire each turn, and input_tokens reflects the whole prompt, not the new part.

The parent-thread replay bug

Now sum it up the naive way.

A usage tool walks the JSONL and adds up input_tokens across every turn it attributes to a given subagent. Because each turn’s input_tokens includes the entire inherited parent context plus everything resent so far, you are counting that shared prefix over and over. Once per subagent turn. Then, across a fan-out, once per subagent. The parent thread gets replayed into each child’s total.

That is the mechanism behind ccusage #950: subagent token counts off by as much as 91x. The shared context that should be counted a single time gets multiplied by the number of turns and the number of agents that inherited it. The heavier your CLAUDE.md, your MCP schemas, your tool definitions, the worse the inflation, because those are exactly the things that live in the inherited prefix and get replayed.

This is why the number can exceed the context window. A count of “more tokens than the model can hold” is not a real quantity of anything. It is the same prefix summed across turns that each resent it.

Why it matters when you run many agents

There is a real practitioner writing up exactly this blind spot: “I Run 5 Claude Code Agents at Once. I Had No Idea What They Were Doing”. Multi-agent runners are flying blind on two questions at once. What did each agent do, and which one burned the quota. A usage tool that inflates per-subagent tokens by up to 91x answers the second question with noise.

I have watched this play out in transcripts. On a ten-agent fan-out over a repo with a large CLAUDE.md and a couple of MCP servers loaded, the naive per-subagent totals summed to several times the real session cost. The agent that looked like the biggest spender was the one that ran the most turns, not the one that pulled in the most new context. The ranking was backwards. If you are deciding which prompt to trim or which subagent to stop spawning, a backwards ranking sends you at the wrong target.

The fix: dedup the inherited prefix

The correct number for a subagent is its marginal cost. The tokens that exist because that subagent ran, not the tokens it inherited from a parent that would have paid for them anyway.

Reconstructing that from the JSONL means walking the transcript as a tree, not a flat list:

  • Identify the sidechain boundaries. Group turns by which subagent produced them, using the sidechain tag and the Task-tool spawn points, so each child’s turns are separated from the parent’s.
  • Find the inherited prefix. The context a subagent starts with is the parent state at spawn time. That prefix is shared, so it belongs to the parent once, not to every child that received it.
  • Attribute the marginal tokens only. For each subagent, count the context it actually added and the output it actually produced. Lean on the cache fields to tell inherited-and-cached prefix apart from freshly generated input.
  • Reconcile against the account total. The children’s marginal costs plus the parent’s own cost should reconcile to what the session really consumed, with no prefix counted twice.

Do that and a subagent can never show more tokens than the model held, and the sum of the parts matches the whole.

TokenJam runs this reconstruction locally, straight off the JSONL already on your disk. Nothing is uploaded, no API key, no signup. The same pass that produces accurate per-subagent attribution also builds the Session Story: a local timeline of what each session and each subagent actually did, so the agent that burned the quota is also the agent whose work you can read back. Which one did the real work, which one spun on a tool loop, which one re-read the same files. pipx install tokenjam (or npx tokenjam / uvx --from tokenjam tj), then tj resume-brief to surface the Session Story snapshot for a session.

TokenJam vs ccusage on subagents

To be fair to ccusage, it is a good tool and it is honest about totals. This comparison is narrow: per-subagent accuracy and the story of a run.

Narrow comparison: subagent accuracy and run replay. ccusage remains strong for totals.
Feature TokenJamccusage
Account and session totals
Accurate per-subagent attribution
Dedups the inherited parent prefix
Session Story (per-subagent replay of what ran)
Reads local JSONL, never phones home
Free via npx / pip
Plan-tier-aware quota reporting

If all you want is the account total for the month, ccusage is fine and you should keep using it. The moment you fan out and need to know which agent to blame, the per-subagent numbers need the dedup step, and that is what TokenJam adds.

For the broader picture of where your quota goes per turn, see where your agent bill actually goes and what agent token economics really means. For the monitoring workflow around all of this, how to monitor Claude Code walks through the setup.

Common questions

Why does one of my subagents show more tokens than the whole session?
Because the tool is replaying the parent thread into that subagent's total. A subagent inherits the parent's context, and each of its turns resends that inherited context, so naive summing counts the shared prefix over and over. That is the ccusage 950 bug, and it can inflate a single subagent by up to 91 times. The real marginal cost is much smaller.
Is ccusage just broken then?
No. ccusage is a good tool and its account and session totals are reliable, because at the total level the inherited prefix is billed as a cache read and mostly nets out. The problem is specific to per-subagent attribution, where that same prefix gets re-counted for every child. Use ccusage for totals and use something that dedups the prefix for per-subagent numbers.
How is TokenJam's per-subagent number different?
It reads the same JSONL but walks it as a tree instead of a flat list. It separates each subagent's turns, identifies the context inherited from the parent at spawn time, and attributes only the marginal tokens each subagent actually added. The children's costs plus the parent's own cost reconcile to the real session total, so no prefix is counted twice.
Does any of this leave my machine?
No. TokenJam reads the transcripts already on your disk under the Claude Code projects folder and does the reconstruction locally. No upload, no API key, no signup. It never phones home.
I run 10 agents at once and have no idea which one burned my quota. Can this tell me?
Yes, that is the point. Accurate per-subagent attribution ranks the agents by real marginal cost instead of by turn count, so the biggest spender is actually the biggest spender. The Session Story sits alongside it as a local timeline of what each agent did, so you can read back the work of the one that ate the window.
Do I need to change how I launch subagents to get accurate counts?
No. The reconstruction runs after the fact on the JSONL Claude Code already writes. You launch agents the way you always do, then point TokenJam at the transcript to get the deduped per-subagent picture and the Session Story.

Get TokenJam updates