Long Claude Code sessions pay for their whole history on every call
On this page
I kept hitting my Claude Code usage limits, so I went through my own agent sessions to find out where the usage went. I had two ideas going in. I blamed the cache, and I trusted the 1M context window. The cache was only a small part of it. The big cost was how much context each session carried.
Every call re-sends the whole conversation
This is what the audit made clear to me. Every action Claude takes is a new call to the model: a tool call, a reply, the next step after a test run. Each of those calls sends the whole conversation so far back to the model.
Prompt caching makes the re-sent part cheaper. A cache read costs about a tenth of the normal input price. It is still billed, on every call.
So the cost of each call grows with the size of the conversation, and the size of the task doesn't matter much. Late in a long session, every small step re-sends everything that came before it.
My sessions made this worse. One session would take an issue, implement it, review it, fix it, then move on to the next issue. By issue seven it was re-sending the test output, review reports and diffs from issues one to six on every call. Issue seven didn't need any of it.
There are two side effects.
Cache misses get expensive. The cache expires after a gap. On a Claude subscription the main session's cache lasts an hour, and subagents get five minutes by default. After that, the next message in the main session writes the whole context to the cache again, at about twice the normal input price. On a context of around 900k tokens, typing "carry on" after more than an hour away is one of the most expensive messages I can send.
Quality drops. Old commit SHAs, finished decisions and earlier restatements of other issues stay in the context. They can leak into the current issue. The model's attention is also less sharp in a very long context.
What the audit showed
I measured my last three weeks of sessions from my own workflow, where an agent takes an issue through to a pull request. I'm only giving percentages and context sizes here, not counts. The percentages are weighted by price, and I explain how at the end.
- About 48% of all my usage was the lead session re-reading its huge context.
- 62% of the lead session's usage happened when the context was above 400k tokens. 87% happened above 200k. Sessions got as high as about 965k.
- Cache misses after a long gap were only about 6.5%. Most of those were me coming back to a session after more than an hour. Some were questions the agent asked me that sat unanswered for a long time.
So the cache miss I suspected was real, but small.
Two smaller findings fit the same pattern. Re-reviews were about 12% of my usage, because every review round re-read the whole pull request. Forks were about 4%. A fork starts with a copy of the lead session's whole context, so even a small forked task pays for everything the lead has seen.
Auto-compaction lowers the cost but doesn't fix it
Claude Code compacts a conversation when it gets near the limit. It replaces the history with a summary and carries on.
One long session on one of my products showed what really happens. The context climbed to about 965k, compaction cut it to about 85k, and it climbed straight back. That happened six times. Its average context per call was about 511k. I split the session into tenths, and every tenth averaged somewhere between 356k and 673k. It never stayed small.
Suppose every call in that session had carried 80k at most, about the size of a session working on one issue. It would have re-read about a sixth as many tokens as it actually did.
Compaction has other problems too:
- It fires wherever the limit happens to fall, which can be in the middle of a review.
- The summary is lossy.
- The summary still drags earlier issues forward into the next one.
Compaction itself is not the expensive bit. While the cache is warm, the summary request reads the history from the cache, so it costs much less than its size suggests.
Start fresh between issues
Between issues I now start a fresh session or run /clear. A fresh session starts with only the system prompt, CLAUDE.md, memory and the tool definitions. That is small, so it is cheap.
/compact would still leave a summary of work that is already finished. Anything worth keeping should already live somewhere durable: the pull request, the issue, a run record or memory. That is the same idea as fixing mistakes in the harness instead of in one chat.
I only use /compact when I'm staying on the same piece of work and the context has got large. I give it a focus so it keeps what matters:
/compact keep the restatement, PR #212 state and the open blockersWhat I changed
I made three changes.
- A lower compaction ceiling. I set
CLAUDE_CODE_AUTO_COMPACT_WINDOW=300k. By my audit numbers, the context should average around 190k per call instead of around 510k. That would make long sessions roughly 60% cheaper. - One agent per issue. My
todoskill and my$devworkflow, the one that takes an issue through to a pull request, run inside Herdr. They hand each issue to its own session and worktree, so each issue gets a session to itself. /clearbetween tasks when I'm working by hand, with/compactand a focus only when I'm staying on the same work.
These are the settings I used or looked at, from the environment variables reference, the model config docs and the prompt caching docs:
CLAUDE_CODE_AUTO_COMPACT_WINDOWsets how full the context gets before auto-compaction, from 100k to 1M tokens. A value like300kneeds v2.1.304 or later. You can also set it asautoCompactWindowin settings.CLAUDE_AUTOCOMPACT_PCT_OVERRIDEsets the trigger as a percentage. It can only lower the threshold.CLAUDE_CODE_DISABLE_1M_CONTEXT=1holds 1M models to a 200K window.promptCacheTtlorCLAUDE_CODE_PROMPT_CACHE_TTLsets the main session's cache to5mor1h.subagentPromptCacheTtldoes the same for subagents, forks and compaction, which default to5m.
How I measured it
Claude Code keeps every session as a JSONL transcript under ~/.claude/projects. Each assistant message has a usage block with input tokens, cache writes (split into five-minute and one-hour), cache reads and output tokens. I included subagent transcripts too.
I read every message and removed duplicates by message id. Then I weighted each kind of token by its relative price: input 1, five-minute cache write 1.25, one-hour cache write 2, cache read 0.1 and output 5. I multiplied that by a rough relative price for each model, which is an assumption. The results are proportions of my usage, not money. I haven't published the scripts.
If you want to check your own sessions, that usage block is the place to start.