[BUG] Oversized plan file makes a session unrecoverable: compaction yields postTokens > preTokens, and resume replays the stale copy
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?
Summary
A plan file that grew to ~2.5 MB (base64-embedded images) is re-attached in full on every turn while in plan mode. That pushed the context from ~512k to ~1.14M tokens and triggered auto-compaction. Because the plan file is re-attached after compaction, the compacted context was larger than before (preTokens: 1142389, postTokens: 1270827), so every following message failed with Prompt is too long. Fixing the plan file on disk did not help, because resume replays the copy stored in the transcript. The only way to recover was to edit the session JSONL by hand.
Evidence (from the session JSONL)
- Last assistant turn before the plan was submitted: total input about 512,536 tokens.
ExitPlanModetool_use line: 2,517,651 bytes (the base64 plan).compact_boundaryentry:"trigger":"auto","preTokens":1142389,"postTokens":1270827- First entry after the boundary: an
attachmentof typeplan_file_reference, 5,032,739 bytes. It holds the full plan twice, once inattachment.planContent(2,515,833 chars) and once inrendered[0].content(2,516,064 chars). /contextthen reported2.4M / 1.0M tokens (241%), with Messages at 2.4M.- After resume: the same stored
plan_file_referenceentry is replayed, and the result is againPrompt is too long.
What Should Happen?
Expected behavior
- Compaction should never produce a context larger than its input. If content that is re-attached after compaction (plan file, memory, etc.) alone exceeds the budget, it should be truncated or summarized, or the user should get an actionable error such as "plan file X is N tokens; shrink it".
- Plan file attachments should have a size cap. Past a threshold, attach the path and a head/tail excerpt instead of the full content, and warn.
- On resume, the plan attachment should be rebuilt from the current file on disk, or at least the user should have a way to refresh it. Otherwise fixing the file cannot rescue the session.
- Nice to have: a way to resume from before the last compaction when the raw history is still in the transcript. In this case the pre-compaction history fit in ~515k tokens once the base64 was removed. Compaction was forced by one bad attachment, not by the conversation itself.
Error Messages/Logs
Prompt is too long, "trigger":"auto","preTokens":1142389,"postTokens":1270827
Steps to Reproduce
- Start a session and work until context usage is about 50% (~500k tokens).
- Enter plan mode. Write a plan file that embeds several PNG images as
data:image/png;base64,...(5 images, ~2.5M characters in total). In my case the assistant did this itself so the images would render in the plan review UI, because local image paths do not render there. - Call
ExitPlanModeand stay in plan mode (leave review comments). - The next turn triggers auto-compaction, then
Prompt is too long. Every later message, including background task notifications, fails the same way. - Shrink the plan file on disk (replace the base64 images with paths), then resume the session. It still fails with
Prompt is too long.
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.281 (Claude Code VS Code extension; standalone CLI is 2.1.185)
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
VS Code integrated terminal
Additional Information
- Environment: VS Code extension via WSL2 remote (Ubuntu 20.04, kernel 6.18.33.2-microsoft-standard-WSL2), Opus with 1M context.
Related issues
- #92434, #85489: re-injected instruction files overflow the window after compaction. Same mechanism, but those are about CLAUDE.md/rules; this one is the plan file, which has no size cap and is attached in full every turn.
- #85483: compaction increases context. Same symptom, different cause (the rebuild floor).
- #92949: an attachment is re-injected from a snapshot instead of the on-disk file. Same class of problem; here it happens on resume, and the plan attachment is stored twice.
Workaround I used
- Backed up the transcript. In the post-boundary
plan_file_referenceentry, replaced bothattachment.planContentandrendered[0].contentwith the shrunk plan. After this, the original session loaded again, but only from the compaction summary. - To get the full history back, copied the pre-boundary lines into a new JSONL with a new
sessionId, and removed the base64 from theExitPlanModeinput. TheparentUuidchain was intact back to the first message. Resuming it worked at ~51% context.
Both steps depend on undocumented transcript internals. This is related to the issues above, but the plan-file path and the stale copy replayed on resume are not covered by any of them.