Claude Code Sessions Bleed Tokens. Here's Where They Go.
A couple of days ago, Claude Code officially published an article specifically about how to improve Session usage efficiency, making every token count.
This is one of Claude's rare, more technically hands-on articles. I felt my adrenaline spike while reading it, because these tips are genuinely too useful.
Let's see what Claude Code actually talked about.
TL;DR
If you find the following too long, you can jump straight to the conclusions:
- Use
/clearbetween different tasks; don't let irrelevant context left over from a previous task be sent to the model along with a new task. - Before starting a task, use
/modeland/effortto confirm the model and thinking intensity. Switching mid-conversation can break the Prompt cache and increase token consumption. - When referencing files, use
@-mention. Don't just tell Claude a filename and let it read it itself; using the @ method directly saves a Read call. - For commands like testing, building, and logging, add quiet parameters whenever possible, or let a Subagent handle them. Command output, like file content, stays in the conversation until the Session ends.
- After starting a new Session, run
/contextonce to see how much contextCLAUDE.md, MCP tool definitions, etc., have already taken up before you even begin. - If you're going to be away from the keyboard, run
/compactfirst. The Prompt cache expires after one hour; compressing the context while the cache is still active costs much less.
(Honestly, after reading these tips, I found them extremely helpful and had to share them properly with everyone.)
Maximizing Token Value
In the past, when writing code, code editors were usually purchased outright, subscribed to, or simply free. Whether you fixed one bug or fifty bugs in an afternoon, the editor didn't bill you per task. This generally refers to the editors from our ancient programming days.
But with Agentic Coding tools like Claude Code, this has changed. For the same task, different usage methods can lead to vastly different token consumption.
(Below are two different Sessions used to verify the comparison of requests and token consumption under different usage patterns.)
In one Session, Claude directly reads the test file and the corresponding source code, runs the test after making changes, and finishes in just a few rounds.
In another Session, Claude first searches the repository, and to find the same two files, it incidentally reads a dozen unrelated files. Moreover, once these files enter the context, they are carried along in every subsequent round.
The final fix result might be the same, but the number of tokens consumed by the two tasks differs greatly.
Therefore, improving token efficiency does not mean blindly using fewer tokens. It means ensuring that every token spent actually plays a practical role.
To understand this, you need to first clarify two questions: one is why some tokens are expensive and others cheap, and the other is why a Session gets heavier and heavier.
What Determines the Price of a Token
Claude bills by the token. What you actually pay for is the inference cost behind the tokens, i.e., how long it takes for a GPU, TPU, or other device to process your tokens.
How expensive a token is depends mainly on three factors: which model is used, whether it is an input or output token, and whether it hits the cache.
Model
The larger the model's parameter count, the more computation is needed to process inputs and generate outputs.
Which task should use which model is a topic worthy of its own article. Claude Code has also published related content (I haven't read that one yet; it depends on whether people highlight it. If anyone is interested, I'll write a separate piece on it).
In this article, you only need to remember one thing: All the token consumption discussed later must ultimately be multiplied by the price of the model itself.
Use larger models only when encountering difficult, ambiguous problems that require complex judgment.
For just changing variable names, running tests, or doing repetitive work, a smaller model is sufficient.
The two images below show a comparison of different models and effort levels for simple tasks (left) and complex tasks (right).
Input and Output Tokens
A single model request goes through two stages, and their costs are different.
The first stage is called prefill. The model first reads all the content in this request, including the system prompt, CLAUDE.md, the message you just sent, and any files and command outputs that have previously entered the conversation. These are all input tokens.
Simply put: Every time Claude Code prepares to answer, or decides on the next tool call, it must first read the current context once.
The second stage is called decode. The model starts generating content, including internal thinking, tool calls like Read or Edit, and the final answer displayed to you. These are all output tokens.
Decode generates tokens one by one. A response containing 200 tokens requires the model to generate 200 times consecutively. This process keeps the GPU working continuously, so the price of output tokens is about 5 times that of input tokens.
The two images below show what input tokens include during the prefill stage (top) and what output tokens include during the decode stage (bottom).
There is also an easily overlooked point here: the output tokens in a Session include not only the final few sentences shown to you but also the thinking tokens used by the model in each round.
/effort controls how much thinking Claude is willing to invest in each round. The more complex the task, the higher the effort needed; if it's just repetitive work, there's no need to let the model add irrelevant consumption—just use a lower effort level.
After starting a new Session, you can run
/modeland/effortonce to confirm the current settings. They remember your last choice, so don't max them out right from the start.If you've already determined that this Session is entirely repetitive, clear, and requires no deep reasoning, you can start Claude Code with MAX_THINKING_TOKENS=0. Claude will turn off the extra thinking budget for this Session. This level is even lower than
/effort low, except for Fable 5.
Prompt Cache
The principle of Prompt cache is actually not complicated.
If the beginning of this request is exactly the same as a request the server just processed, the server doesn't need to recalculate that part—this is Prompt cache. It can directly load the previously saved state and only perform prefill on the newly added content.
The price of a cache read is about 0.1 times that of a normal input token. Writing tokens into the cache for the first time is a bit more expensive, potentially up to 2 times the normal input price, because the server needs to save the state in addition to computing.
Claude Code automatically manages the Prompt cache; you don't need to manually enable it. But the problem is, although the cache is managed automatically, it can also be actively destroyed by human actions.
The article uses utils.test.ts as a concrete example.
Suppose we input:
Fix the failing test in utils.test.ts
The whole process is like this:
▲ Five model requests behind a small fix: historical context goes through cache, new content is appended round by round.
- Claude Code first assembles the tool definitions, system prompt,
CLAUDE.md, and your message into the first request. The cache is still empty at this point, so all inputs must be fully prefilled and written into the cache. - The model hasn't seen the file that needs testing yet, so it can't modify it directly. It thinks for a moment and then generates a Read tool call. Tool calls are output tokens. Claude Code reads the file, appends the Read call and file content to the end of the conversation, and then initiates the second request (these are all input caches). This time, the content already present in the previous step is priced at 0.1x, and the only things requiring full-price tokens are the new content for prefill: the Read tool call and the file.
- After reading the test file, the model still needs to read the corresponding source code file, so it generates another Read call. Claude Code reads the second file, continues appending to the end of the conversation, and initiates the third request. Content from the first two rounds continues to use the cache; the second file is new content and requires normal prefill.
- After reading both files, the model generates an Edit tool call. Claude Code executes the modification, appends the Edit and the modification result to the conversation, and initiates the fourth request. Previously existing content continues to use the cache, and the new content added in this round is processed at the normal input price.
- Next, the model generates a tool call to run
npm test. Claude Code appends the test output to the conversation and initiates the fifth request. At this point, only the new test results need normal prefill. - The test passes, and the model generates a short summary. Since there are no new tool calls this time, there are no new tool results to append, so no sixth request is generated, and the task ends here.
That is to say, what looks like a very simple prompt above actually triggers five model requests behind the scenes. Each request carries the complete conversation up to that point.
From this, it's clear that each round of requests is extremely asymmetric: the input might have tens of thousands of tokens, while the output only has a few hundred.
The token cost for each round roughly consists of three parts:
- Historical context: calculated at the cache read price.
- New content added in this round: calculated at the normal input token price.
- Model-generated thinking, tool calls, and answers: calculated at the output token price.
Even if you are using a subscription plan, this mechanism still exists. You might not see the specific price of each token, but these requests will tangibly consume your usage quota.
Prompt cache must match from the beginning of the request. When Claude Code sends a request, the front part is usually the tool definitions, system prompt, and then the conversation content starting with CLAUDE.md. If the front part changes, all subsequent content must be re-prefilled. Appending tool results at the end is the most ideal situation because it doesn't change anything in the front.
The operations that truly risk invalidating the cache are the following:
/model: Each model has its own cache, so switching models mid-conversation in a long dialogue might require re-prefilling the entire session at the normal price in the next round./effort: The effort level is also part of the cache key. Modifying effort midway has a similar effect to switching models.- Fast mode: Whether Fast mode is enabled is also a cache matching condition. Enabling Fast mode midway means the old cache can no longer match, and the entire session will be re-prefilled at the Fast mode price. So if you want to use it, it's best to use it from the very beginning of the Session.
/compact: Context compression; only the preceding system prompt can be retained, the context is compressed. However, as long as the old conversation is still in the cache, the cost of context compression is not high. So when preparing to be away for a long time, it's best to compact first, then leave.- Time: The cache time is recalculated in each round. Under subscription mode, the cache usually expires after one hour; for API keys, the default is five minutes, but setting the
ENABLE_PROMPT_CACHING_1H=1parameter can extend it to one hour. If this time is exceeded, the next round often requires re-prefilling the entire conversation. Resuming an old Session is usually the same, because the cache has most likely expired, and the system prompt is also rebuilt at startup.
This doesn't mean model, effort, or Fast mode can never be switched. The cheapest time to switch is at the very beginning of a Session, or right after executing /clear. The most expensive time is suddenly switching in the middle of an already very long conversation...
If the last few rounds have gone off track and you don't want to keep them, you can use
/rewindto go back to before things went wrong. It only cuts off a few rounds from the end, and the preceding content still matches the cache.In contrast,
/compactrewrites the entire conversation, inevitably generating new costs.
How Many Tokens Does a Session Actually Send
Files Claude has read and outputs from commands it has run will be sent again in every subsequent round until the Session ends.
Most of them can hit the cache, so repeated sending is relatively cheap. But cheap doesn't mean free. More importantly, this content continuously occupies the context window, and the model must include them in its thinking every round.
Ultimately, a Session's cost model is considered like this: how many tokens have entered the context, how many rounds of conversation they stayed in, and how many contexts you are running simultaneously.
What Kind of Content Enters the Context
Before you type your first sentence, there is already stuff in the context: tool definitions, system prompt, CLAUDE.md, and other content loaded at startup.
You can run
/contextin a newly opened Session to see what's already in the context before you start working. Try to keepCLAUDE.mdto only specific, long-term valid rules. Instructions needed only for a certain workflow can be placed in a Skill and loaded when needed. If a particular MCP server is not needed for the current task, turn it off via/mcp.
After the Session starts, what most easily fills up the context are basically tool call results: files Claude reads, and the output generated by commands it runs.
How many files Claude will read depends on how many problems it decides to solve on its own.
If you just say "The test failed," it first has to find out which test failed. It might run one or two greps first, then open several files to judge which ones are relevant.
If you directly say:
Fix the failing test in utils.test.ts
Claude will skip the search and directly Read this file.
If you take it a step further and write:
Fix the failing test in @utils.test.ts
The file will be directly attached before the first message is sent, saving even the Read call.
(I really hadn't thought of this detail before; using the @-mention method saves a Read call.)
Using
@-mentionversus letting Claude Read itself, the context space occupied by the file itself is actually the same. The difference is that@-mentionsaves the search and tool call. Mentioning the same file once in a conversation is enough, because it will stay in the context. @-mentioning it again later usually attaches another copy of the same file.
Another major context overhead culprit is command output.
Every time Claude runs a test, build, or git log, the content printed to the terminal is appended to the conversation like a file and persists in every subsequent round.
After exceeding 30,000 characters, Claude Code writes the full output to a file and only keeps a small preview and the file path in the conversation. This threshold can be modified via BASH_MAX_OUTPUT_LENGTH.
The more troublesome cases are those outputs that are under 30,000 characters but are long and messy.
For example, a test tool prints 400 passing tests line by line. Because it doesn't exceed the limit, these 400 lines enter the context verbatim and then accompany you through the entire Session...
Claude usually uses quiet flags or tail on its own to control output. If you don't want to leave control to them, the official documentation also has a Hook that can automatically rewrite commands before they run, leaving only the truly important content.
You can write the two or three most commonly used daily commands directly into
CLAUDE.md, including the quiet flags. For example: "Usenpx vitest run <file> --reporter=dotto run a single test file." It seems like a small change, but it can save one round of execution in every subsequent Session and also prevent hundreds of lines of output from being stuffed in.
For the same task, stuffing it into a very long Session is usually more expensive than splitting it into several new Sessions. Because round 40 doesn't just process your conversation for that round; it also has to re-read the context cache of the previous 39 rounds.
So, try to keep the context in the current Session short and relevant to the main thread. Use /clear when starting a new task; use /compact when a complex task enters a new phase and the previous process is no longer that important.
The image below shows the cost of each task after using /clear (top), and the cost of a long Session (bottom).
If you want to find the current Session again later, you can run
/renamebefore/clear. When using/compact, it's best to explicitly tell Claude which content must be retained; if the content to retain is always the same, you can also add a "Compact instructions" section inCLAUDE.md. When using a 1M context model, if you want to restore the auto-compact threshold to the previous 200k, you can run/autocompact 200k, but this requires Claude Code v2.1.221 or higher.
You also need to pay attention to rounds without input.
For example, /loop. Each loop executes a full round in the current Session, carrying the entire conversation. If more than an hour has passed since the last round, it will also encounter a cache miss and re-prefill the entire context.
Therefore, for loops that need to run long-term, it's best to open a clean Session in another terminal.
Subagent
There is another way to reduce context pollution: put the task into a Subagent.
A Subagent has an independent context window, its own system prompt, tools, and CLAUDE.md, but it doesn't get the full conversation of the main Session. It completes the task within its own context and only returns the final answer to the main Session. The files read in between, the logs generated by executing commands, and its own thinking process are all discarded after the task ends.
This sounds great, right? The problem is, the Subagent cannot see the main conversation and sometimes has to re-read files that the main Session has already read, and its own rounds also consume tokens.
So, throwing small tasks to a Subagent might just add extra overhead for nothing.
Scenarios more suitable for Subagents are tasks that generate a large amount of process information but where the main Session only needs the final conclusion, such as analyzing a very long log.
You can directly tell Claude:
Analyze this log in a Subagent and only return the error cause and the relevant line numbers.
This way, thousands of lines of logs only stay in the Subagent's context, and the main Session might only receive a few sentences in the end.
If there is a type of high-noise task that needs to be repeatedly handed to a Subagent, you can create a separate Subagent for it and specify the model as
haikuorsonnet. Otherwise, it will default to using the model currently used by the main Session.
So, after all this rambling above, there are really only four things worth prioritizing for inspection, ranked from highest to lowest cost:
- Session is too long. Every round resends all previous content; this is where most tokens are most easily spent.
- Too much stuff crammed into the context. Irrelevant files, useless command output, content left over from the previous task, and unused MCP servers are all noise.
- Model or effort exceeds task requirements. The more expensive the model, the greater its thinking token overhead; it's like using a sledgehammer to crack a nut.
- Breaking the Prompt cache midway. Switching models, effort, or Fast mode in a long conversation, or coming back after the cache expires, can cause the entire session to be re-prefilled, losing the original 0.1x cache pricing.
My biggest takeaway after reading this article is that Claude Code's token optimization is truly hidden in the details.
If I had to remember just one sentence, I think it should be: One Session does one thing, and only keeps the context truly needed to complete that thing.
Top 3 from juejin.cn, machine-translated. The original thread is authoritative.
Full of useful insights, learned a lot
This approach is indeed practical
This tool is so practical, it solved the big problem I had with previewing files~