[BUG] Auto-compact appears to decide from the previous turn's token count, so re-injected instruction files overflow the window instead of compacting
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Resuming a session whose project carries a large instruction set can fail immediately with prompt is too long, with no compaction attempted, even though the session was below the auto-compact threshold on the previous turn. Running /compact by hand right afterwards succeeds and the session continues normally, so compaction was available. It just did not happen on its own.
What this looks like from the outside: resuming re-reads and re-injects CLAUDE.md and .claude/rules/*.md into the request. Where the instruction set is large, that is a substantial amount of content added to a turn that has not been measured yet. The auto-compact decision appears to be made from the previous turn's reported token count, which cannot include what this turn is about to add. The client therefore sees headroom, the API sees a request over the limit, and the turn ends in a 400 rather than in a compaction.
I have seen this on several sessions on 1M-window Opus models, and every failing turn carries a large re-injected instruction attachment while the turns that succeed do not. Being careful about the size claim: the instruction files are the largest single component of what gets re-added on resume, but not the only one, and in the clearest case they account for roughly a third of the jump. So the trigger is not simply "instruction set exceeds remaining headroom" and I cannot give an exact threshold. Two shapes get large enough to matter, neither unusual: many small files accumulated under .claude/rules/, or a single CLAUDE.md grown by appends over months. I have only seen the failure with a combination of both, so I am not claiming either reaches the failing size alone.
What Should Happen?
Two possible remedies, in rough order of preference:
- The compaction decision should be evaluated against the request as it will actually be sent, rather than against the previous turn's reported count. Any content the harness is going to add before dispatch needs to be inside the number the check consults.
- Failing that, a
prompt is too longon a request that the harness itself inflated should route into reactive compaction and be retried, rather than ending the turn and waiting for the user to type/compact.
Independently of either: when a request is rejected for length, reporting how much of it was injected instructions would point users at the cause. Nothing currently distinguishes "your conversation got too long" from "the instruction files we just re-added did not fit", and the natural response to the first reading is to discard a session that only needed one compaction.
Error Messages/Logs
prompt is too long: 1043785 tokens > 1000000 maximum
The previous turn on that session reported roughly 646k tokens used, so the resume added around 398k. The instruction set is the largest share of that but not all of it, which is why I describe the mechanism rather than assert a formula.
Steps to Reproduce
The 200k-window case is much cheaper to set up than the 1M one, so it is the better starting point.
- In a scratch project, create instruction files large enough to matter against your model's window. The content is irrelevant because the files are injected verbatim:
````
mkdir -p .claude/rules
for i in $(seq 1 40); do
yes "filler line, content is irrelevant to this reproduction" | head -500 > ".claude/rules/r$i.md"
done
Do not give these files a paths: frontmatter block. Rules without paths: are loaded unconditionally; path-scoped rules only load when Claude reads a matching file, and will not reproduce this.
- Work in the project until
/contextshows the session close under the auto-compact threshold, without going over it. On a 200k window that threshold is reported at roughly 167k, so a few large file reads are enough to get near it. - Exit, then resume the session (
claude --resume, orclaude --continue). - Send any short message.
- The request fails with
prompt is too long, no compaction is attempted, and the session cannot proceed./compactthen succeeds immediately.
Flagging what I have not isolated: I saw this after session restarts and after re-authentication, and #91709 reports the same symptom after --resume. I have not established which reload paths re-inject the instruction files, so step 3 may need more than one attempt.
Claude Model
Opus
Is this a regression?
I don't know
Claude Code Version
2.1.261 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
Observed on 2.1.261 and also on 2.1.257. 2.1.263 shipped while this was being written and is untested.
Related reports. None cover this as far as I can tell, but the neighbourhood is crowded, so I would rather name them than have them found for me:
- #91709 is the closest, and I read it as corroboration rather than duplication. Same symptom, same platform and window, failing on a short message after
--resumewith roughly 100k of window left. The difference is the decisive one: that error string endsauto-compact is off, so nothing there was expected to compact. In my case auto-compact is enabled and the session is under its own reported threshold, and still nothing compacts. - #85489 reports the same re-injection behaviour with the opposite outcome, compaction firing repeatedly rather than not at all. Both can end in a stuck session, so the symptoms overlap; what differs is the sequence, a request rejected on resume before any compaction versus repeated refilling after one. Happy for these to be merged if maintainers see a single root cause.
- #91385 and #91232 ask for a usable warning before the window limit is reached, which overlaps the diagnostic suggestion above.
- #86699 proposes a per-project memory size cap as a percentage of the context window. That would reduce exposure to this without addressing where the decision is made.
- #86507 describes sanitized 400 bodies disabling reactive compaction. That is the Bedrock gateway leg rather than this platform, but it is adjacent to remedy 2.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Resume re-injects CLAUDE.md then 400s because auto-compact used last turn's token count. I keep standing rules in Vestige so the host file dump is not the only copy. Measure the request you will send. I already have the capsule.
https://github.com/samvallad33/vestige
Sharp diagnosis: auto-compact deciding from the previous turn’s count cannot see the CLAUDE.md /
.claude/rulesre-injection this turn is about to add, so you getprompt is too longwith headroom on paper and a working/compactright after.Two product-shaped fixes you listed are the right order — measure the request as-sent, or route length-400 into reactive compact+retry. Independently, the error should say how much of the overshoot was re-injected instructions vs conversation. Today “conversation got too long” and “we just re-added a giant Current rule set” look identical, and people discard sessions that only needed one compact.
Related failure mode in other issues: when the instruction set is huge, people split into path-scoped rules — but those only load on
Read, so the “Current” rules that survive are not always the ones that govern the subtree you are in. Measuring the inject before dispatch would at least make the overflow honest.Adding one constraint to the remedies above, because I want it explicit rather than implied. Today, as far as I can tell, starting or resuming a session does not dispatch a model request until the user sends a message, and that is worth preserving: launching the CLI should not by itself consume quota or tokens. So I am not suggesting that a resumed session compact eagerly on launch. The suggestion is that the compaction decision belongs at the moment the first message is sent, when the harness knows what it is about to inject and can measure the request it will actually dispatch, which is remedy 1 above with the timing made explicit. A UI notice at that point, for example that the resumed context plus the re-injected instruction files exceed the window and a compaction will run before the message goes out, would be fine and probably helpful. What I want to rule out is any default where opening a session immediately starts spending tokens before the user has asked for anything, even in the service of avoiding this failure.