Skip to content

[Bug] Auto-compaction fires at ~447k on a 1M-context model after model switch and fails with compaction.interrupted #57392

Open

Description

@thumb2086

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 --version on the CLI shim reports 2.0.32, the running server logs version=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

  1. Have a long session (~447k tokens according to the previous model's usage) using opencode/beam-free.
  2. Switch the session model to opencode/deepseek-v4.1-flash-free (same provider).
  3. 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 compaction message is created ~1.05s after the user message.
  • Its status is failed, reason auto, error compaction.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 under beam-free and ~861k under space-bunny-free without the provider rejecting.

  • The free deepseek-v4.1-flash-free is not actually capped at ~200k. The 200k value comes from models.opencode.ai, but real usage exceeded 447k and earlier sessions reached 861k on space-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:

  1. 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.
  2. 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

Activity

  1. opencode-agent commented on Oct 20, 2026

    @opencode-agent
    Contributor

    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-free and 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:

    • With your limit.context: 1000000 and buffer: 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/model reports. 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.interrupted is 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:

    • Did you press Esc or cancel the turn, or did the server or app restart around that moment?
    • The server log lines around the compaction (Windows log directory, opencode server logs), especially any error or interrupt entries.
    • Does it happen without the 1M override, i.e. with the model's default limit?

    Related issues:

  2. ExTreeMe7 commented on Nov 6, 2026

    @ExTreeMe7

    Additional data point (v2.2.5, Windows, anomalyco/opencode release build). Same symptom as the report, but without an explicit model switch.

    • Model: custom OpenAI-compatible provider, limit: { context: 1048576, output: 128000 } (no limit.input), so cc/Kv window = 1048576.
    • compaction config at the time: { "auto": true, "buffer": 20000 } (token buffer), so the size threshold was ~1,028,576 (~98%).
    • Auto-compaction fired at measured 436,128 tokens (~41.6%) of the 1M window.
    • Immediately before it: session.revert.stage / session.revert.commit (twice), then a very short user message, then config.updated / provider.updated / model.updated, then the compaction ~seconds later.
    • Recorded as { status: "failed", reason: "auto", error: { type: "compaction.interrupted" } }. No provider "too long" / "longer than context length" error in the logs.
    • Changing compaction.buffer from 20000 to "15%" did not affect the early trigger.
    • The session had earlier used several models behind different providers (two custom openai-compatible providers plus the built-in opencode provider), 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% (measured 1,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 auto eligibility check (hc inside ed) only compares providerID (not model.id, variant, or agent), while other paths do compare those.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions