Repository navigation
Description
Summary
Auto-compaction fired at ~447k conversation tokens on a model configured with a 1,000,000-token context, about 1 second after sending a message, and then failed with compaction.interrupted. The session had just been switched from opencode/beam-free to opencode/deepseek-v4.1-flash-free (same provider). The UI showed ~447.6K (43%).
This may be related to the catalog-vs-gateway limit mismatch in #50574 / #56296, but the model-switch timing and the full-estimate behavior look like a separate bug.
Environment
- OpenCode: 2.1.0 (
opencode --versionon the CLI shim reports 2.0.32, the running server logsversion=2.1.0) - OS: Windows 11
- Provider/model:
opencode/deepseek-v4.1-flash-free - Config (
~/.config/opencode/opencode.jsonc):
{
"compaction": { "auto": true, "keep": { "tokens": 24000 }, "buffer": 16000 },
"providers": {
"opencode": {
"models": {
"deepseek-v4.1-flash-free": {
"limit": { "context": 1000000, "output": 128000 }
}
}
}
}
}The running server's /api/model reports limit: { context: 1000000, output: 128000 } for this model.
Reproduction
- Have a long session (~447k tokens according to the previous model's usage) using
opencode/beam-free. - Switch the session model to
opencode/deepseek-v4.1-flash-free(same provider). - Send a short message.
Expected
Either:
- the request is accepted and the session continues, or
- if the provider rejects it as too long, a single overflow-recovery compaction runs and completes.
Actual
- A
compactionmessage is created ~1.05s after the user message. - Its status is
failed, reasonauto, errorcompaction.interrupted("Compaction was interrupted"). - The session goes idle with outcome
interrupted. - No assistant response is produced.
Evidence
Session message rows (read-only from opencode.db):
seq 143529 type=assistant model=opencode/beam-free
tokens: input=280 cache.read=446592 output=732 reasoning=6 (usage ≈ 447,610)
seq 143538 type=model-switched beam-free -> deepseek-v4.1-flash-free
seq 143541 type=system (3,541 chars)
seq 143551 type=system (8,980 chars)
seq 143561 type=system (14,561 chars)
seq 143571 type=system (10,560 chars)
seq 143572 type=user (46 chars)
seq 143573 type=compaction status=failed reason=auto
error: {"type":"compaction.interrupted","message":"Compaction was interrupted"}
seq 143575 type=idle outcome=interrupted
The session had ~165 tools loaded (godot 71, unityMCP 48, open_browser_use 19, blender 9, open_computer_use 9, opencode 8, blindfold 1).
Additional context (from follow-up)
- I did not press Esc or cancel the turn; the TUI was idle when the compaction started.
- Server log around that moment (
~/.local/share/opencode/log/opencode.log) has no ERROR/WARN for compaction, only:
timestamp=2026-10-20T11:56:09.579Z level=INFO run=73cd3b04 message=session.revert.stage sessionID=ses_ec9ed0effffezdoXCLpbtIqGZK ...
timestamp=2026-10-20T11:56:09.736Z level=INFO run=73cd3b04 message=session.revert.commit sessionID=ses_ec9ed0effffezdoXCLpbtIqGZK
timestamp=2026-10-20T11:56:10.867Z (compaction event created, status=failed, reason=auto, error=compaction.interrupted)
-
Without the 1M override (default 200k), auto-compaction would trigger around 184k (
200000 - 16000), i.e. ~2.4x earlier. I overrode to 1M because the free model plainly serves more than 200k: this session had already reached ~447k underbeam-freeand ~861k underspace-bunny-freewithout the provider rejecting. -
The free
deepseek-v4.1-flash-freeis not actually capped at ~200k. The 200k value comes frommodels.opencode.ai, but real usage exceeded 447k and earlier sessions reached 861k onspace-bunny-free(1M catalog) with no 400. The catalog under-reports real capacity (tracked in [FEATURE]:increase session limit of deepseek V4.1 flash the free model #56034 / compaction: 1M-context sessions never auto-compact before the provider returns HTTP 400, and the 400 is not treated as context overflow #50574). The override to 1M is a workaround; the early compaction here is a side effect. -
Could maintainers confirm the real input limit for
opencode/deepseek-v4.1-flash-free? If it is genuinely ~450-500k, updating the catalog to that value (and letting the overflow classifier surface the provider body) would let users avoid both the premature 200k compactions and the surprise compaction at 447k.
Hypothesis
In SessionCompaction.compact, the auto preflight check is:
if (reason === "auto" && !F(context, nm(context.model.limit, buffer))) return { status: "skipped" }nm(limit, buffer) uses limit.input || limit.context minus buffer, i.e. 984,000 here. The measured usage is ~447k, so the preflight threshold should not fire. Two possibilities:
- The request was rejected by the gateway as too large (real free-tier input limit lower than the advertised 1M), and the overflow-recovery path triggered the compaction. The failed event is recorded with
reason: "auto"regardless. - After the model switch, the usage estimator falls back to a full request estimate (system + all 165 tool schemas + all messages), which can exceed the threshold even though the provider-reported conversation usage is ~447k.
Either way, the user-visible result is an early compaction at 43% followed by compaction.interrupted, rather than a completed compaction or a usable session.
Related
- compaction: 1M-context sessions never auto-compact before the provider returns HTTP 400, and the 400 is not treated as context overflow #50574 — 1M-context sessions and compaction ceiling / catalog-vs-provider limit
- opencode-go: long sessions stuck at provider input limit; auto-compaction never triggers #56296 — opencode-go long sessions stuck at provider input limit; auto-compaction never triggers
- [FEATURE]:increase session limit of deepseek V4.1 flash the free model #56034 — Zen's ~200k context for the free DeepSeek V4.1 Flash
- [FEATURE]: per-model compaction threshold (reserved/buffer) #43703 — per-model compaction threshold (reserved/buffer)
- keep.tokens is not honoured: the compaction split walk-back is unbounded and skips synthetic messages, so agent-driven sessions carry 234K past a 15K setting #43250 —
keep.tokensnot honoured
Activity
opencode-agent commentedon Oct 20, 2026
Thanks for the detailed report and the DB rows, @thumb2086.
I couldn't reproduce this here. It depends on the real OpenCode Zen free model
opencode/deepseek-v4.1-flash-freeand a session of roughly 447k tokens, and this sandbox has no provider access. A simulated model can't show which context limit the session actually uses.What I found in the 2.1.0 code:
limit.context: 1000000andbuffer: 16000, auto-compaction should only start at about 984k. But your rows show the compaction created right after your message, before any model call, which is where that pre-call check runs. That would fit the session using a smaller limit than/api/modelreports. For reference, Zen's own limit for this free model is about 200k (see [FEATURE]:increase session limit of deepseek V4.1 flash the free model #56034).compaction.interruptedis only recorded when the compaction is interrupted, for example by cancelling the turn or the server stopping, not when it fails on its own.Things that would help a maintainer with Zen access:
opencodeserver logs), especially any error or interrupt entries.Related issues:
ExTreeMe7 commentedon Nov 6, 2026
Additional data point (v2.2.5, Windows,
anomalyco/opencoderelease build). Same symptom as the report, but without an explicit model switch.limit: { context: 1048576, output: 128000 }(nolimit.input), socc/Kvwindow = 1048576.compactionconfig at the time:{ "auto": true, "buffer": 20000 }(token buffer), so the size threshold was ~1,028,576 (~98%).measured436,128 tokens (~41.6%) of the 1M window.session.revert.stage/session.revert.commit(twice), then a very short user message, thenconfig.updated/provider.updated/model.updated, then the compaction ~seconds later.{ status: "failed", reason: "auto", error: { type: "compaction.interrupted" } }. No provider "too long" / "longer than context length" error in the logs.compaction.bufferfrom20000to"15%"did not affect the early trigger.opencodeprovider), consistent with the stale/anchor measurement mismatch described in core: compaction trusts a measured prompt from a different model behind the same provider #61372.A later auto-compaction in the same session fired normally at ~99% (
measured1,038,932), so it is intermittent and tied to the revert / config-model refresh rather than a plain size overflow.Bundle note that matches #61372: in the 2.2.5 chunk, the measured anchor used by the
autoeligibility check (hcinsideed) only comparesproviderID(notmodel.id,variant, oragent), while other paths do compare those.