Repository navigation
Description
Description
Long-running sessions can grow their recorded token usage without a practical upper bound, especially through tokens.cache.read, until most models/providers fail with a context-window error and the session becomes unrecoverable.
In my real session, OpenCode reported approximately 56,128,248 total tokens while the persisted conversation data was small:
session tokens:
input: 14,774,671
output: 77,881
reasoning: 20,400
cache.read: 41,255,296
cache.write: 0
total: 56,128,248
stored rows for that session:
message: 224 rows, ~0.52 MB JSON data
part: 1042 rows, ~2.33 MB JSON data
After this, continuing the same session fails with:
Your input exceeds the context window of this model. Please adjust your input and try again.
The issue is not that the saved message JSON itself is tens of millions of tokens. The large number appears to come from cumulative session usage accounting. In particular, tokens_cache_read can grow very large over repeated turns.
Relevant code path observed in source:
packages/core/src/session/projector.ts
tokens_input: SessionTable.tokens_input + value.tokens.input
tokens_output: SessionTable.tokens_output + value.tokens.output
tokens_reasoning: SessionTable.tokens_reasoning + value.tokens.reasoning
tokens_cache_read: SessionTable.tokens_cache_read + value.tokens.cache.read
tokens_cache_write: SessionTable.tokens_cache_write + value.tokens.cache.write
This makes session-level usage an ever-growing lifetime counter. That is fine for statistics, but it becomes dangerous if any compaction / routing / provider request / context-window logic treats cumulative session usage, especially cache.read, as if it represented the current prompt/context size.
Impact:
- Long sessions eventually hit provider context-window errors.
- Auto compaction does not reliably save an already-overflowed session.
- Once overflowed, the same session can repeatedly fail and effectively becomes unusable.
opencode statscan become slow/hang on large databases/sessions.- This affects providers/models with smaller context windows especially badly when using an
automodel/router.
Expected behavior:
- Current context-window checks should be based on the actual messages that will be sent to the provider, not lifetime cumulative usage counters.
cache.readshould not be counted as current prompt/context tokens for overflow decisions.- Auto compaction should trigger before the provider rejects the request.
- If a provider returns a known context-window error, OpenCode should attempt a safe compact/prune/retry or suggest/fork a new session instead of leaving the session dead.
- There should be a hard cap / safety guard for long-running sessions, e.g. archive/fork/reset usage or force compaction before sending.
Plugins
Only a local/custom provider/router was used (providerID: 9090, modelID: auto). No plugin-specific behavior is required to reproduce the core issue: repeated turns can accumulate session usage far beyond any real model context window.
OpenCode version
1.15.13
Steps to reproduce
- Use OpenCode with a provider/model that reports token usage including cache read tokens.
- Keep a long-running session active for many turns, especially with repeated cache hits or provider-side prompt caching.
- Inspect the SQLite session row, for example:
select
tokens_input,
tokens_output,
tokens_reasoning,
tokens_cache_read,
tokens_cache_write
from session
where id = '<session id>';- Observe that cumulative
tokens_cache_readand total session tokens can grow to tens of millions even when the stored message/part JSON is only a few MB. - Continue the same session until the provider rejects the next request with a context-window error:
Your input exceeds the context window of this model. Please adjust your input and try again.
- Try enabling aggressive compaction settings such as:
{
"compaction": {
"auto": true,
"prune": true,
"tail_turns": 1,
"preserve_recent_tokens": 4000,
"reserved": 12000
}
}The already-overflowed session still remains difficult/impossible to continue.
Screenshot and/or share link
No public share link, because the affected session contains private project data. Sanitized measurements are included above.
Related existing issues that look adjacent but do not mention the cache.read/cumulative-session-usage aspect:
- compaction loop and hitting context limit 200k on single request #29450 compaction loop and hitting context limit 200k on single request
- Auto-compaction enabled by default, but context_length_exceeded errors still occur in agent workflows #8089 Auto-compaction enabled by default, but context_length_exceeded errors still occur in agent workflows
- [BUG]: /compact can exceed model context before summarization on large sessions #29857 /compact can exceed model context before summarization on large sessions
- prompt is too long unrecoverable #4845 prompt is too long unrecoverable
Operating System
Windows 11
Terminal
Windows Terminal / PowerShell
Activity
github-actions commentedon Jun 4, 2026
This issue might be a duplicate of existing issues. Please check:
processor.tsincludingcache.readhandling, with proposed fixes that overlap with the root cause described here.wang1970 commentedon Jun 4, 2026
Thanks for the pointers. I checked both suggested issues and I think this report is related but not a straight duplicate.
Clarification:
processor.ts: whether fields are overwritten vs accumulated within one assistant message. It explicitly treatscache.readas a per-step/session snapshot-like value that should not be summed within one multi-step message.session.tokens_cache_read. In my case the stored session row reached:while the persisted content was only:
So the main distinction is:
cache.readmay be reasonable as a provider usage/cost metric, but it should not be allowed to make the session's effective context accounting / compaction threshold / context display / recovery behavior look like tens of millions of tokens when the actual stored prompt material is only a few MB.cache.read-dominated session counter growth. My report adds a concrete 1.15.13 measurement and the breakdown showing that the inflated value is dominated by cached reads rather than actual message text.If this should be merged into one of those issues, the key extra signal to preserve is: session-level cumulative
tokens_cache_readcan dominate token totals and make context/compaction behavior diverge from real prompt size. A likely fix is to separate billing/usage totals from effective-context accounting, and exclude/handle cache-read tokens differently for context-window/compaction decisions.wang1970 commentedon Jun 14, 2026
Bump — this issue has been open for 10 days without any maintainer response.
To add urgency: the unbounded
cache.readaccumulation is not just a cosmetic counting bug. It directly causes massive token over-consumption in real usage. Sessions that should have only consumed a few hundred thousand tokens are being accounted as tens of millions, which triggers premature context overflow, failed compaction, and forces expensive re-sends of entire conversation histories.For anyone hitting the same problem: every long session is silently burning tokens at an inflated rate. This is a cost and usability regression that affects all OpenCode users with non-trivial session lengths.
Could a maintainer take a look? The core fix seems straightforward — separate billing/usage totals from effective-context accounting, or exclude
cache.readfrom context-window/compaction decisions. Happy to help test or provide more data if needed.Necmttn commentedon Jun 21, 2026
The fix likely needs two counters, not one corrected sum. Keep cumulative billing/usage counters separate from the active context-window counters used for eligibility and compaction decisions.
cache.readcan be meaningful for cost and provider accounting, but it should not make the next prompt look like it contains 41M active tokens. A good regression would assert that repeated cached turns increase billed/cache usage while active context stays bounded by retained messages plus current tool/schema overhead.Generated with ax.
AdityaPagare619 commentedon Jun 23, 2026
Same thing on my end. Session had 937M cache read tokens but only ~41MB of
actual data. The cumulative counter was way out of sync with reality.
This made the context overflow from Bug #3 worse — the session thought it
was already over the limit when the actual context was still manageable.
royruho commentedon Jul 8, 2026
Confirming this from the user-facing side, and adding a version boundary that may help narrow it.
The additive accumulation described here (session token fields, especially cache.read, summed every turn rather than reflecting current context) appears to be what surfaces as #34712: in Web/Desktop the input/context count is inflated and /compact doesn't bring it down, while the TUI shows the correct current-context number for the same session. That lines up with pruning messages being unable to decrement a cumulative counter.
Version boundary on my end:
1.17.11 — count behaves correctly, no inflation
1.17.12 / 1.17.14 — inflated count, /compact doesn't reduce it
mcj777 commentedon Aug 10, 2026
Bug #30649 confirmed:
cache.readinisOverflow()— source analysis and timelineWe performed a full analysis of bug #30649 based on
anomalyco/opencodesource (branchdev, HEAD:fe82a1b6, 2026-08-08) and git history. Confirmed: the bug is present and unfixed.Root cause
File
packages/opencode/src/session/overflow.ts:32:cache.readrepresents tokens served from the provider's prompt cache — they were not transmitted in the current request. DeepSeek and MiMo return 10-41M+ tokens incache.readper step. Withcontext=128K,usable≈96K: 10M ≫ 96K →isOverflow()always returnstrue→ false compaction at ~16% actual context fill.Introduction timeline
3aa28600(Brendan Allan, 2026-06-30) —overflow.tsandprojector.tscreated from scratch. Thecache.read + cache.writeline present since day oneConfirmed manifestations
Workaround (source-verified)
Two short-circuit paths in
overflow.ts:28-29bypass the bug without code changes:compaction.auto: falseinopencode.json— disablesisOverflow()entirely (line 28)limit.context: 0in model configuration — model-level short circuit (line 29)Both verified: false compactions stop.
ContextOverflowErrorfrom the API remains active as an honest safety net.Fix
Line
overflow.ts:32: replacecache.read + cache.writewithcache.write— a one-line fix.cache.readtokens are NOT part of the context window and should not influence compaction decisions.Zaczero commentedon Aug 18, 2026
The lifetime-counter observation still holds on
0.0.0-beta-17498— the session totals are cumulativeadds, exactly as quoted, at
projector.ts:59-63.The part worth checking is the conditional: "dangerous if any compaction / routing / provider request /
context-window logic treats cumulative session usage as if it represented the current prompt size." I
grepped every reader of those columns to find out whether anything does.
Where they get read, and what for
Four places read
tokens_input,tokens_cache_readand their siblings. Every one of them is adisplay or transport path:
info.ts:38-46decodes them intoSession.Info.tokens— the shape the API and TUI render.projector.ts:71-95selects them and publishesSessionEvent.UsageUpdated. That event is declaredEvent.ephemeral, so it isn't even persisted, and nothing in core subscribes to it; it exists topush a number at connected clients.
transfer.ts:113-117carries the totals along when a session moves between instances.v1-migration.bun.tsbackfills them once from per-message tokens.Nothing in compaction, routing, provider request assembly or context-window accounting consults them.
Compaction in particular reads the last assistant message, never the session row:
opencode/packages/core/src/session/compaction.ts
Lines 367 to 386 in 39be159
So the 41M cumulative
cache.readin the original report can't reach the compaction trigger. Mydatabase bears that out from the other direction: sessions carry lifetime
cache.readin the billions— 6.74B on
openaialone — while their per-messagecache.readtracks the real prompt size andcompaction fires normally throughout.
Which leaves two separate things
The counters are a presentation problem. The number in
opencode statsis a lifetime sum that readslike a context measurement, and relabelling or documenting it would remove the alarm without changing
any behaviour.
The context-window failure is a different bug that this report may have misattributed. If it still
reproduces on V2 it deserves its own issue, and the useful diagnostic is the last assistant message's
tokens at the point of failure rather than the session total — that's the number the trigger actually
sees. Worth checking #42448 too, which has compaction itself blowing the context window through an
unbounded output budget; that failure looks a lot like this one from the outside.
thdxr commentedon Oct 11, 2026
Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (
opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.— from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊
AdityaPagare619 commentedon Oct 12, 2026
Make sure to handle the Session growth properly as the newer version still has some issues for exporting sessions and also session growth is making the models thinking weaker somehow after reaching certain parameters!