On Claude Code 2.1.289 with Opus 5.5, a
/compactof a 136k-token session took 136,275 of its 138,344 input tokens from the prompt cache and cost $0.056 to $0.059 at list price, about two ordinary turns, while the same compaction after the cache had expired cost $0.53 to $0.54, and a/compactsent right after the turn that read the files paid full price for those files ($0.30 to $0.32 at 85k tokens)./clearsent no API request in 13 of 13 runs, and the first request after it was 15,303 tokens, against 14,254 to 14,923 after a warm compaction.
The costs page of the Claude Code docs has a list of reasons why usage climbs in a long session, and the last item is about compaction. It says that /compact "reads the conversation it summarizes, so compacting a large context is itself a large request", and then: "When you want a fresh start instead of continuity, /clear costs nothing". Both sentences are true, and neither one gives a number. How large is the request? Is it large in tokens, in dollars, or both? What does the turn after it cost, and how does that compare with clearing and handing the next session a short note?
We measured it. The lab built sessions of three sizes by having Claude read real JavaScript files, then compacted or cleared them, and read the usage of every API request from the request and response bodies that Claude Code can write to disk. Thirty-eight claude -p processes ran on 2026-10-05 (UTC) with Claude Code 2.1.289 (claude --version) and --model opus, which resolved to claude-opus-5-5. In an earlier article we measured what instructions survive a compaction. This one is about what it costs.
What the docs say
The costs page, fetched at 16:20 UTC on the day of the runs, comes back to compaction several times. In the section on reducing token usage: "Claude Code automatically optimizes costs through prompt caching, which reduces costs for repeated content like system prompts, and auto-compaction, which summarizes conversation history when approaching context limits." In a list of messages that are not usage limits, it calls the auto-compact window "the threshold where Claude Code summarizes older history to free space." And in the list of reasons usage climbs, the two sentences quoted above. On /clear, it also says the session cost figure resets: "These totals reset when /clear starts a new session, so the next session's total cost starts at $0."
The costs page links the phrase "compacting a large context" to the prompt caching page, which describes the request in detail:
To produce the summary, Claude Code sends a separate request with the same system prompt, tools, and history as your conversation, plus a summarization instruction appended as a final user message. While the cache is warm, that request reads your prefix from the cache, so a mid-session
/compactcosts a fraction of what the context size suggests and spends most of its time generating the summary.
The next paragraph covers the cold case: "After a break longer than the cache lifetime, there is no cache left to read, so the summarization request reprocesses the full history as uncached input. This is why /compact costs the most when you resume an old session." It ends with: "In both the warm and cold cases, the turn after compaction rebuilds the conversation cache for only the much shorter summary, so that turn is not the slow part." The same page puts compaction in the "Everything else" bucket for cache lifetime, which defaults to five minutes apart from a few server-controlled helper requests, and lists FORCE_PROMPT_CACHING_5M=1, "which forces five minutes for both buckets", for debugging cache behavior. We used that variable for the cold runs.
The costs page names no setting for when automatic compaction runs; it links to the model configuration page, which lists four places to set the auto-compact window: the /autocompact command, the autoCompactWindow setting, the --autocompact flag for one launch, and the CLAUDE_CODE_AUTO_COMPACT_WINDOW environment variable, which "takes precedence over the command, the flag, and the setting." Without one, the default for our model is: "Models running with a native 1M window compact before the window fills, at about 967K tokens by default. On the Anthropic API, these include Sonnet 5, the Fable models, and Opus 4.7 and later." Two more environment variables bear on it. CLAUDE_AUTOCOMPACT_PCT_OVERRIDE sets "the percentage (1-100) of the auto-compact window at which auto-compaction triggers", and "the variable can't raise the threshold." DISABLE_AUTO_COMPACT turns the automatic pass off, and "The manual /compact command remains available."
One more statement matters for the next turn. The context window page says that right after compaction, Claude Code "re-reads up to five of the files Claude has read or edited in the session, choosing the ones modified most recently. A file over 5,000 tokens comes back as a path reference without its content".
The lab
The content is real code: JavaScript files of 31 to 41 KB copied from open-source packages (Jest's transform build, Babel's scope tracker, argparse, browserslist, a Handlebars compiler, minimatch, picomatch). Every session starts with one message that asks Claude to read a set of these files with the Read tool, all in one message, and then to describe each file in ten words. One file, four files and seven files gave three sizes. Measured as the full input of the last request before compacting, the sessions held 34,176 to 34,185 tokens, 84,639 to 84,666 tokens, and 136,344 to 136,364 tokens. Of that, about 19k, 69k and 120k tokens were file contents, and about 15k was what any session starts with: system prompt, tool definitions and the opening reminders.
Each message after the first was one of these:
- the probe, a cheap question that needs the earlier context: "Without using any tools, reply in one line: the names of the files you read earlier in this conversation, comma-separated, or NONE if you cannot tell."
-
/compactor/clear, sent as a plain prompt string - in the handoff runs, "Write a handoff note for a fresh session to HANDOFF-.md ... at most 150 words", and after
/clear, "Read HANDOFF-.md with the Read tool, then reply in one line" with the file names
The main shapes were: build, probe, /compact, probe (compact); build, probe, /clear, probe (clear); and build, probe, write note, /clear, read note (handoff). Each ran twice at each size, eighteen runs. The probe before the command is there for two reasons: it measures what an ordinary turn costs at that size, and it makes the session end with a short exchange, the way a session usually does at a natural break. Two fresh sessions sent only the probe, as a control.
Then the variants, two runs each: compacting after a cold cache at 85k and 136k (with FORCE_PROMPT_CACHING_5M=1 and 6.5 minutes of silence before /compact); sending the probe after the same silence without compacting, at 136k; sending /compact immediately after the build turn, with no probe in between, at 85k; automatic compaction at 85k with --autocompact 100000; and the four-file session rebuilt from 24 small files of 1.4 to 9.1 KB (about 90k tokens), to see the re-read of files under 5,000 tokens.
Each session ran in one claude -p process with --input-format stream-json. A small Node script wrote one user message, waited for the result event, and wrote the next. That is the shape the Agent SDK docs describe for manual compaction: "Send /compact as a prompt string to trigger compaction on demand." The flags were --model opus --setting-sources project,local --strict-mcp-config with an empty MCP config, plus --allowedTools Read Write, and auto memory was off. A wrapper removed environment variables inherited from the Claude Code session that started the runs, including any auto-compact settings.
The numbers come from the request bodies. With CLAUDE_CODE_ENABLE_TELEMETRY=1 and OTEL_LOG_RAW_API_BODIES=file:<dir>, Claude Code writes every request and response to disk and appends a line per request to <dir>/index.jsonl with a query_source field. The summarization request is tagged compact; the main conversation in these runs was tagged sdk. Each response carries the API's usage block: uncached input_tokens, cache_read_input_tokens, cache_creation_input_tokens split into five-minute and one-hour writes, and output_tokens.
Two other records agreed with the bodies. Each --debug-file log had one line per compaction, such as Forked agent [reactive-compact] finished: 1 messages, types=[assistant], totalUsage: input=2069 output=1168 cacheRead=136275 cacheCreate=0, and it matched the response body in 19 of 19 compactions. And the change in total_cost_usd across the /compact step matched the bodies' usage priced at the Opus 5.5 list prices: $4 per million input tokens, $5 and $8 for five-minute and one-hour cache writes, $0.20 for cache reads and $20 for output (the pricing page, fetched at 16:36 UTC). The session transcript was the one place without it: it holds the summary and a compact_boundary record with preTokens and postTokens, but no usage record for the summarization request, whose tokens appear only inside the session totals of the closing cost-state record.
Every dollar figure below is that list-price estimate, the same one Claude Code shows. The main conversation's cache writes were one-hour writes, which the prompt caching page gives as the default on a Claude subscription.
The compaction request, warm
| Context before | Ordinary turn | Compaction input (cached / uncached) | Summary output | Compaction cost | Cost vs. ordinary turn |
|---|---|---|---|---|---|
| 34.2k | $0.0078 | 36,169 to 36,175 (34,100 to 34,106 / 2,069) | 1,084 to 1,440 | $0.037 to $0.044 | 4.7x to 5.7x |
| 84.7k | $0.019 | 86,636 to 86,654 (84,567 to 84,585 / 2,069) | 994 to 1,055 | $0.045 to $0.046 | 2.3x to 2.4x |
| 136.4k | $0.031 | 138,344 to 138,357 (136,275 to 136,288 / 2,069) | 1,000 to 1,168 | $0.056 to $0.059 | 1.8x to 1.9x |
Both halves of the docs held, 6 of 6. The request is large in tokens: it carried the whole conversation except its last short reply, plus about 2,000 tokens, 1.01 to 1.06 times the context it summarized. It is not large in dollars while the cache is warm, because all but 2,069 of those tokens were cache reads. The 2,069 were the probe question, the summarization instruction and a one-line token-budget reminder. The instruction was the same 6,361 characters in every run. It begins "CRITICAL: Respond with TEXT ONLY. Do NOT call any tools." and asks for "an block followed by a
block" with nine sections, from "Primary Request and Intent" to "Optional Next Step". It went out as a second text block inside the last user message, with the same thirteen tool definitions and the same system prompt as the conversation.At 136k tokens, the $0.0589 of one compaction splits into $0.027 of cache reads, $0.023 of summary output and $0.008 for the 2,069 new tokens. Priced as uncached input, the same context would have cost $0.545 to process once, so the warm compaction came to 10 to 11% of that. At 34k the share was 27 to 32%, because the instruction and the summary do not shrink with the session. That is the docs' "a fraction of what the context size suggests", measured: about a tenth at 136k, about a third at 34k.
The summary barely grew with the session. Claude Code recorded postTokens of 1,549 to 1,977 after compactions of 34k to 136k tokens, and the summaries were 3,183 to 3,676 characters long. All six named every file that had been read, and the probe after compaction answered with the full list in 6 of 6 runs. The compaction itself took 9.3 to 12.7 seconds.
The compaction request, cold
The cold runs repeated the compact shape with all cache writes at five minutes and 6.5 minutes of silence before /compact.
| Context before | Warm compaction | Cold compaction | Cold: uncached input | Cold vs. warm |
|---|---|---|---|---|
| 84.8k | $0.045 to $0.046 | $0.326 to $0.333 | 75,336 to 75,338 | 7.0x to 7.4x |
| 136.5k | $0.056 to $0.059 | $0.529 to $0.542 | 127,019 to 127,032 | 9.0x to 9.8x |
The docs held here too, 4 of 4: the summarization request "reprocesses the full history as uncached input". Only 10,466 to 11,447 tokens, the shared system prompt and tool definitions, still came from the cache. A cold compaction cost 96 to 99% of what reading the whole context once at the base input rate costs.
One detail kept that from being worse. The warm summarization requests each carried a cache breakpoint on a message of the conversation; in the four cold runs, no message had one. So the 127,000 tokens were billed as plain input at $4 per million, not as cache writes at $5 or $8.
The two runs that waited without compacting show what the alternative costs. After the same 6.5 minutes, the probe at 136k tokens rewrote 125,158 to 125,159 tokens to the cache and cost $0.629 in both runs, against $0.53 to $0.54 for the cold compaction plus $0.021 to $0.023 for the turn after it. After a long break, then, compacting cost slightly less than simply continuing, and the turns after it read about 15k tokens instead of 136k. Our cold runs used five-minute writes so the cache would expire within minutes. With the default one-hour writes on a subscription, the cache lasts an hour, and the rewrite in the continuing case is priced at $8 per million, which comes to about $1.00 for those 125,158 tokens. We computed that figure and did not run it.
The expensive warm compaction: right after the big turn
The compact runs had one ordinary exchange between the turn that read the files and /compact. The immediate runs did not: build, then /compact, then the probe. The cache was warm, and the compaction still cost almost as much as a cold one.
In both immediate runs, the compaction request took 16,110 tokens from the cache and 70,327 uncached, and cost $0.303 and $0.315. The automatic runs, with --autocompact 100000, produced the same request: 16,128 cached, 70,353 uncached, $0.305 and $0.307, trigger: "auto" in the compact_boundary record. That is 6.5 to 7.0 times the $0.045 to $0.046 of the warm compactions at the same size.
The request bodies show why. In 18 of our 19 compactions, the most recent assistant reply was not in the summarization request. It came back verbatim after the summary, recorded under preservedMessages in the boundary record, which fits the Agent SDK docs' description of compaction "keeping your most recent exchanges and key decisions intact". The request's cache breakpoint sat on the last assistant message that was being summarized. In the compact runs, that message was Claude's description of the files, so everything up to it, file contents included, was a cache hit, and only the probe and the instruction were new. In the immediate and automatic runs, the reply being preserved was that description, and the last summarized assistant message was the one that called Read. The roughly 68,000 tokens of file contents came after it, and were sent uncached. (The nineteenth compaction, a setup test whose conversation was a single "Reply OK" exchange, kept no reply and summarized everything.)
The automatic runs also showed when the automatic pass fires. With --autocompact 100000, the debug log reported effectiveWindow=80000, against effectiveWindow=980000 in all 35 other runs that wrote a debug log. When the reads came back and the session stood at 84,486 tokens, the log said level=compact and then Reactive compact: no assistant messages in summarize set, bailing, so the request with the file contents went out uncompacted and Claude answered. The compaction ran when the next prompt arrived, before it was sent. The session ended up where the immediate runs were: a summarization request whose newest tool results sat after the cache breakpoint.
We read this as a rule about position, not about manual versus automatic: what the summarization request reads uncached is whatever follows the last summarized assistant message. In our immediate runs that was roughly 68,000 tokens of file contents. In a real session it is the tool results of the last step before the final reply, which can be small, a test summary, or large, a big read or a long log. We did not test other shapes than these two.
The next turn
| First request after | Tokens | Cost | Probe answer |
|---|---|---|---|
| a fresh session | 15,180 | $0.034 (new prefix) or $0.003 (cached) | NONE, 2 of 2 |
/clear |
15,303 | $0.034 (new prefix) or $0.003 (cached) | NONE, 6 of 6 |
/compact, warm, large files |
14,254 to 14,923 | $0.026 to $0.032 | the file list, 6 of 6 |
/compact, warm, small files |
28,316 to 29,287 | $0.143 to $0.151 | the file list, 2 of 2 |
/clear, then reading a handoff note |
15,301, then 15,824 to 15,949 | $0.044 to $0.046 for both requests | the file list, 6 of 6 |
| no action | 34,176 to 136,364 | $0.008 to $0.031 | the file list |
After /clear, the next request was the same as a fresh session's plus 123 tokens: the first user message carried a record of the /clear command itself, a caveat line, the command name and its empty output. Nothing from the cleared conversation was in it. That is also why its cost varied: in the five runs whose post-clear prompt had already been sent by an earlier run, the whole request was a cache hit for $0.003; in the first run it had to write 3,979 tokens to the one-hour cache, for $0.034.
After a warm compaction, the request was smaller than a fresh session's, even with the summary in it. Two things went missing. The skill listing, 6,805 characters of the opening attachments in our setup, was not sent again; the context window docs say as much ("this listing is not re-injected after /compact"). And the files the session had read came back as one-line notes instead of their contents, one per file up to the limit of five, since each was over 5,000 tokens:
The system prompt and tools, 11,322 tokens, were read from the cache in all six runs, as the prompt caching page says. The rest, 2,930 to 3,599 tokens, was written to the cache, because the summary is new text, so the first turn after a compaction always cost $0.026 to $0.032 here. That is the "rebuilds the conversation cache for only the much shorter summary" of the docs.
The small-file runs show the other side of the file rule. With 24 files of under 5,000 tokens each, Claude Code re-read the five most recently modified ones in full after compacting. postTokens rose to 9,817 and 10,438, the next request to 28,316 and 29,287 tokens, and that turn wrote 17,000 to 18,000 tokens to the cache for $0.143 and $0.151, more than twice the $0.054 and $0.072 the compactions cost. The bill for compacting can be mostly the turn after it.
/clear costs nothing, and a handoff note does not
/clear made no API request in 13 of 13 runs. It returned in 0.05 to 0.34 seconds, with a conversation_reset event, a new session id and total_cost_usd: 0. The docs' sentence held exactly.
What it gives up is continuity: the probe after /clear answered NONE in 6 of 6 runs. The usual way to keep some of it is to have Claude write a short note before clearing and read it afterwards. In our handoff runs that cost more than compacting at every size. Writing the note took two requests, the Write call and the reply, and both read the whole context from the cache. Reading it back after /clear took two more.
| Context before | Compaction + next turn | Write note + clear + read note |
|---|---|---|
| 34.2k | $0.063 to $0.072 | $0.074 to $0.075 |
| 84.7k | $0.075 to $0.076 | $0.097 |
| 136.4k | $0.087 to $0.091 | $0.119 to $0.120 |
The request that wrote the note produced 486 to 599 output tokens, fewer than a summary, and the session after reading it was 15.8k to 15.9k tokens, about the size of a session after compaction. Its advantage is that it barely depends on the conversation's cache. Reading a note into a fresh session cost $0.044 to $0.046 in every run, and after a break the most that could change is the shared 11,322-token prefix of system prompt and tools going cold, which we did not test. A compaction after a break cost $0.53 to $0.54 at 136k.
When compacting is worth it
What follows is our reading of the numbers. The arithmetic uses the Opus 5.5 prices above; we did not run long sessions to test it.
A compaction pays for itself through smaller requests afterwards. Each later request reads the post-compaction context from the cache instead of the full one, and at $0.20 per million cache-read tokens the saving per request is small. At 136k, a request saves about $0.024, and the compaction and the cache write after it come to about $0.058, so it pays back after two or three requests. At 85k, it takes about four. At 34k, about fifteen. A turn that uses tools makes several requests, so in a working session the larger cases pay back within a turn or two.
So our working rules, from this lab:
- If you are switching to unrelated work, use
/clear. It sent nothing, and the next request was the size of a fresh session. - If you want to keep going on the same work in a large session, compact at a natural break, after a short reply, not straight after the turn that pulled in a large tool result. In our runs that was the difference between $0.045 and $0.31 at the same size.
- If you are about to step away for longer than the cache lifetime, compact or write the note before you go. Compacting the same session after the break cost nine times as much, and continuing it cost more still.
- Check how big your recent files are. Files under 5,000 tokens come back in full after compacting, and with five of them the first turn after compaction cost more than twice the compaction.
- On a small session, compaction barely pays. At 34k, the next turns would only shrink from 34k to 14k tokens, a saving of $0.004 per request.
What we did not measure
Everything ran on one machine, one account, one model (Opus 5.5) and one version (2.1.289), headless, through stream-json input. We did not run claude -p "/compact" --resume, which our earlier article found behaves differently for skills, and we did not open an interactive session. Other models have other prices, and the cache-read discount on Opus 5.5 (5% of the input price, per the pricing page) is steeper than the 10% on most others, which makes warm compaction look cheaper here than it would elsewhere.
The sessions were short: one big read, a probe or two. Real sessions have many turns, edits and tool calls, and their summaries may be longer than our 924 to 2,285 output tokens. We did not test /compact with focus instructions, sessions near the 967K default threshold, or the background summary that the monitoring docs say auto-compaction can prepare ahead of time.
The automatic runs used the --autocompact flag. Our first two attempts set CLAUDE_CODE_AUTO_COMPACT_WINDOW instead, but our wrapper removed that variable before Claude Code started, so those runs never compacted and we cannot say anything about the variable. We also did not test the autoCompactWindow setting, /autocompact, CLAUDE_AUTOCOMPACT_PCT_OVERRIDE or DISABLE_AUTO_COMPACT, and we did not work out the exact threshold behind effectiveWindow=80000.
The cold runs forced five-minute cache writes; the one-hour case is computed. The cost figures are list-price estimates. On a subscription, what counts is usage against plan limits, which we did not measure. The raw-body files, the index.jsonl index and the debug-log line are what we read; the transcript records only part of this, and none of these formats is guaranteed to stay the same.
Thirty-eight processes in all: the eighteen main runs, two fresh sessions, the fourteen variant runs (including the two that never compacted), two setup tests, one pilot and one test of the script below. They made 19 compactions and 13 clears. Claude Code reported $26.78 in total_cost_usd for all of them, 80% of it cache writes, mostly the file contents going into the cache.
Reproduce it
The driver sends each message to one claude -p process and waits for the turn's result before sending the next:
// drive.mjs: send messages to one `claude -p` process, waiting for each result before the next.
import { spawn } from 'node:child_process';
import readline from 'node:readline';
const claude = spawn('claude', ['-p', '--input-format', 'stream-json', '--output-format', 'stream-json',
'--verbose', '--model', 'opus', '--setting-sources', 'project,local', '--strict-mcp-config',
'--mcp-config', 'empty.json', '--allowedTools', 'Read', '--debug-file', 'debug.log'],
{ stdio: ['pipe', 'pipe', 'inherit'] });
let resolveTurn;
readline.createInterface({ input: claude.stdout }).on('line', (line) => {
const ev = JSON.parse(line);
if (ev.type === 'result') {
console.log(JSON.stringify({ session: ev.session_id, cost: ev.total_cost_usd, result: ev.result }));
resolveTurn();
}
});
for (const text of process.argv.slice(2)) {
await new Promise((resolve) => {
resolveTurn = resolve;
claude.stdin.write(JSON.stringify({ type: 'user', message: { role: 'user', content: text } }) + '\n');
});
}
claude.stdin.end();
process.exit(0);
Put a large file next to it as big.js and run:
echo '{"mcpServers":{}}' > empty.json
export CLAUDE_CODE_ENABLE_TELEMETRY=1 OTEL_LOGS_EXPORTER=console OTEL_METRICS_EXPORTER=none
export OTEL_LOG_RAW_API_BODIES=file:$PWD/bodies CLAUDE_CODE_DISABLE_AUTO_MEMORY=1
node drive.mjs "Read big.js in full, then describe it in one line." \
"Which file did you read? One line." "/compact" "Which file did you read? One line."
grep -o 'reactive-compact\] finished.*' debug.log
jq -r 'select(.query_source=="compact") | .response_file' bodies/index.jsonl \
| while read f; do jq -c '.usage | {input_tokens, cache_read_input_tokens, output_tokens}' "bodies/$f"; done
In our run of this script (with a run label added to the first message) and a 41 KB file, the summarization request read 34,054 tokens from the cache and 2,032 uncached. Drop the second message to see the expensive case, or replace /compact with /clear and check that bodies/index.jsonl gains no line between the two answers. If you start it from inside another Claude Code session, unset the variables that session exports, such as CLAUDECODE, first.
The driver and the jq line above are all it takes to price one /compact on your own sessions, and to see whether your last turn left a big tool result after the cache breakpoint.
If your compaction request reads a very different share from the cache, post your cached and uncached numbers in the comments below.


Top comments (1)
The finding that an immediate compaction right after a heavy tool return costs seven times more than one following an assistant reply isolates where the prompt cache breakpoint actually sits. Developers intuitively treat cache state as continuous across a session. In practice the breakpoint anchors to the previous assistant turn, which turns the tool payload into an unhedged basis risk where the caller pays full uncached rates on the exact tokens they were trying to compress.
The second-order economic consequence is the payback hurdle on cold resumes. Saving twenty-four mils per request at a hundred and thirty-six thousand tokens breaks even in three turns against a six-cent warm compaction, but stretches to twenty-two turns against a fifty-four-cent cold compaction. In sprint work where an engineer resumes an old branch to verify a patch and closes the shell, compacting after the cache expires has negative expected value compared to reading a thirty-token handoff note into a fresh session.