[BUG] `advisor` tool doubles the reported context size, firing auto-compact at ~50% of the real window
Summary
Every turn that calls the server-side advisor tool reports an usage block
roughly 2× larger than the turn before it, because the advisor's own
forwarded-transcript prompt is summed into the same usage block as the main
turn. Claude Code treats that sum as context occupancy, so a conversation that
is really at ~85k tokens is accounted as ~175k and auto-compact fires.
Measured across 20 transcripts of one project: 27 advisor turns, 27
doublings, no unexplained doubling, and 7 of the 16 auto-compactions in
those transcripts happen on an advisor turn — each time with a real
conversation of only 82–100k tokens.
The generic mechanism is already acknowledged in the Anthropic cookbook
("Automatic context compaction" → Limitations and Considerations →
Server-Side Sampling Loops: *"Cache tokens accumulate across sampling loops,
which can trigger compaction prematurely based on cached content rather than
actual conversation history"*). That section lists server-side web search and
server-side extended thinking as affected; advisor is not listed, and I
could not find any issue reporting it. Filing this because the advisor case is
fully deterministic and easy to measure.
Environment
- Claude Code 2.1.220 (latest published at time of writing)
- Linux 6.14, bash
- Main model:
claude-opus-5;"advisorModel": "opus"in~/.claude/settings.json - Also reproduced on
claude-opus-4-8transcripts - No
[1m]/ extended-context model involved — plain 200k windows only
Controlled reproduction
Fresh session, no large tool results, exactly one advisor() call. Consecutive
turns from ~/.claude/projects/<project>/<session>.jsonl:
line 110 12:08:12 input 2 + cache_creation 935 + cache_read 52907 = 53,844 (Edit)
line 114 12:08:14 input 4 + cache_creation 1691 + cache_read 108008 = 109,703 (server_tool_use: advisor)
- ratio 2.038
2 × 53,844 = 107,688; observed109,703→ +2,015 tokens of
advisor-specific overhead (the reviewer system prompt). Everything else is an
exact duplicate of the main prompt.
The two seconds between the turns contained a single Read whose tool_result
was a few hundred bytes. Nothing entered the conversation that could account for
+55,859 tokens.
Proof the number is not real occupancy
One transcript reports 382,597 input tokens on claude-opus-4-8 — a 200k
window, non-[1m]. A single prompt of that size cannot exist. It is~191k main + ~191k advisor summed into one usage block.
Proof the auto-compact trigger reads this number
Every compact_boundary in the 20 transcripts whose immediately preceding turn
was an advisor turn:
| session | advisor turn (reported) | previous turn (real) | ratio |
|----------|-------------------------|----------------------|-------|
| 8cb8bc81 | 182,352 | 89,508 | 2.04 |
| 8cb8bc81 | 198,342 | 97,051 | 2.04 |
| ab157ad4 | 180,153 | 88,055 | 2.05 |
| ab157ad4 | 202,197 | 99,892 | 2.02 |
| b5af4b0d | 177,771 | 87,059 | 2.04 |
| b5af4b0d | 170,207 | 81,980 | 2.08 |
| d4d07771 | 195,019 | 96,453 | 2.02 |
A conversation of 82–100k crosses no threshold on a 200k window. The only value
that does is the sum. In the b5af4b0d / 170,207 case the compaction fired
2 seconds after the advisor turn, on a conversation whose previous turn was
81,980 and whose intervening tool_result was 294 characters.
Steps to reproduce
claudewith an Opus main model and"advisorModel": "opus".- Work normally until the session reaches ~85k tokens.
- Call
advisor(). - The context indicator jumps to ~175k and auto-compact fires immediately.
Or measure it on existing transcripts:
import json, glob
def total(u):
return (u.get('input_tokens', 0)
+ u.get('cache_creation_input_tokens', 0)
+ u.get('cache_read_input_tokens', 0))
for f in sorted(glob.glob('*.jsonl')): # in ~/.claude/projects/<project>/
prev = None
for line in open(f):
try: d = json.loads(line)
except ValueError: continue
m = d.get('message') or {}
u = m.get('usage')
if not u:
continue
t = total(u)
if t == prev:
continue
c = m.get('content')
advisor = isinstance(c, list) and any(
b.get('name') == 'advisor' or b.get('type') == 'advisor_tool_result'
for b in c
)
if prev and t > prev * 1.7:
print(f'{f[:8]} {prev:>7} -> {t:>7} ratio={t/prev:.2f} advisor={advisor}')
prev = t
Expected behaviour
The context-occupancy figure that drives auto-compact (and the status line)
should count only the tokens that are actually in the conversation, excluding
the tokens consumed by the nested server-side advisor request. The advisor's
cost is a real billing cost, but it does not persist in the context window —
the next turn's cache_read confirms this, since it returns to the
pre-advisor baseline plus the small advisor result block.
Actual behaviour
The summed usage is treated as occupancy, halving the usable window on any
session that uses the advisor, and firing auto-compact mid-task at ~50%.
Notes
- The binary already contains a
retry:advisor-strip/
tengu_advisor_strip_retry path that replays a request with advisor blocks
removed, so advisor content is already recognised elsewhere as
prompt-inflating.
- Worst case is Opus main + Opus advisor (the only combination available when
the main model is Opus, since the advisor must be at least as capable): the
duplicate is exactly the size of the main prompt.
- Existing workarounds, both lossy: disable the advisor (
/advisor→
Advisor disabled, or CLAUDE_CODE_DISABLE_ADVISOR_TOOL), or disable
auto-compact (autoCompactEnabled: false) and /compact manually. Raising
CLAUDE_CODE_AUTO_COMPACT_WINDOW is not a workaround: only advisor turns are
doubled, so a non-advisor turn would then be allowed to grow past the real
200k limit.
Possibly related
- #50204 — same ~2× factor and same
.jsonlmethodology, and advisor calls
appear in the repro steps, but attributed to the display denominator on a
[1m] model. Closed as duplicate, no maintainer comment, no link to the
canonical issue.
- #64923 — auto-compaction at ~50% reported utilisation, cause unverified.
Closed as duplicate.
- Advisor issues covering unrelated symptoms (400s / availability): #49994,
#56515, #64158, #67609.
3 Comments
Same behavior here on 2.1.229, with one detail that narrows the cause.
The doubling is not permanent. It reverts on the next turn.
advisor().No content leaves the session between step 3 and step 5. The context cannot shrink on its own. So the value in step 3 is a display error, not real usage.
Auto-compact reads the same counter. In a long session it fires early, and it discards context that the session still needs. My test session sat too low for auto-compact to fire, so I confirm the meter error only.
Related: #81029 and #84738 report the same symptom. #84738 proposes that the advisor turn sums usage across its internal iterations. That cause fits the revert in step 5, because the next turn recomputes the total from the message history.
Video posted below:
https://github.com/user-attachments/assets/410083ff-39e7-4670-b369-084d18da2479
Same bug with auto-compact off: the session stops instead of compacting
We run
autoCompactEnabled: false, so we never see the spurious compaction. We get theother end of it. 2.1.227,
model: opus[1m],advisorModel: opus, macOS.One session, consecutive records:
Real context about 521k against a 1M window. The session stopped mid-task and needed a
manual
/compactto continue.Not a one-off. Across 5,729 local transcripts: 210 hit "Prompt is too long", 113 of
them mid-session, and 37 of those ended the session. 32 of the 37 had an advisor call
within 12 records, against 41 of the 76 that recovered.
The ratio reproduces independently, over 53 advisor turns in 14 transcripts: 2.03x
median, p90 2.06, 51 of 53 above 1.8x, against 1.01x for 4,207 ordinary turns of which
none exceeded 1.8x. Two details fit the iteration-sum cause in #84738 rather than an inline
second copy: the growth is entirely
cache_readwhile freshinput_tokensgo 1 to 4, andit reverts to 1.03x on the next turn.
One thing I cannot establish from outside: Whether that error is a real API rejection or a
local refusal: no
isApiErrorMessagerecord of any kind carries arequestId(0 of 13here, including
server_errorones that certainly came from the server).Your 27/27 doubling study matches — #84738 pins this mechanism with transcript-level evidence: a turn that invokes the advisor reports usage summed across
usage.iterations— two full-context message iterations, so top-level usage comes out at ~2× real context (e.g. cache reads 515,122 + 515,905 → 1,031,027 reported) — and auto-compact eligibility reads the rolled-up figure. Quantified over 8 days of transcripts on one machine: every subagent auto-compaction in the window fired on the turn immediately after an inflated advisor rollup, at real contexts of 310–517K, average (preTokens − real context) ≈ 350–390K. Subagent seats consulting the advisor at checkpoints are hit hardest.