Skip to content

Auto-compaction can be a fixed point (never shrinks context) → eternal compaction/continue loop with no user-visible exit #52697

Closed as not planned

Description

@harkers

Summary

Auto-compaction + the synthetic post-compaction Continue… turn can produce an eternal loop on tool-heavy sessions: each "compaction" leaves net context ≥ 0, so isOverflow() stays true and compaction re-fires every single step, while each continue-turn re-primes the model to re-emit its most recent completed tool call. opencode's DOOM_LOOP_THRESHOLD guard only counts repeated tool parts within the current assistant message (processor.ts, parts.slice(-3)), so a cross-message loop is invisible to it.

Reproduction (observed live, not synthetic)

opencode 1.18.34, model local/qwen3.8-27b-15g (declared limit: {context: 65536, output: 16384}). PR-review task with several ~3.5K-char GitHub tool outputs. From the event store (27 summary messages, 26 compaction parts):

  • Turn inputs: 49,417 → 50,835 tokens, monotonically increasing across 26 compactions — compaction never shrank anything.
  • Per cycle: tool call → step-finish → compaction part (auto: true, overflow: false) → summary assistant message (~2K input/~200 output via a cloud compaction agent) → synthetic user part ("Continue if you have next steps…") → the same tool call again.
  • 13+ identical worktree-verify completions before manual abort; ~80 s and ~50K tokens burned per cycle; cached_tokens: 0 every turn.

Root cause (source read, this version)

  1. usable() = context − maxOutputTokens = 65536 − 16384 = 49,152. A ~50K-token session is genuinely over it — the loop starts once history crosses this line and never leaves it.
  2. Compaction cannot reduce context here:
    • preserveRecentBudget() = min(15000, max(2000, usable × 0.25)) = 12,288 tokens of recent turns always kept in the tail;
    • tool outputs within PRUNE_PROTECT (40K) of the end are exempt from pruning;
    • in the observed session select() returned tail_start_id: undefined for all 26 compactions (head/tail overlap → everything summarized is also kept);
    • each cycle additionally adds the summary assistant message (~200 output tokens) + synthetic continue part.
      Net effect: context_after ≥ context_before — the overflow state is a fixed point. Compaction fires forever.
  3. The synthetic Continue… turn lands right after a summary whose tail contains the just-completed tool call(s); small local models pattern-match the last-seen safe action instead of executing their stated next step. Since repeat detection is per-message only, nothing trips.

Expected

  • Compaction should be net-negative on context for the session that triggered it, or auto-compaction should refuse to re-arm while the previous compaction failed to reduce isOverflow() (the second compaction is a no-op → surface an explicit error/idle instead of looping).
  • The doom-loop detector should consider the last N assistant messages, not just parts of the current one — a compaction boundary is exactly where the naive in-message window fails.
  • The synthetic continue turn should not be appended when the immediately preceding stretch shows the same completed tool call ≥ threshold times across messages.

Observed impact

Eternal ~80 s/turn loop at ~50K tokens/turn with no prefix cache; manual abort was the only exit; no user-visible progress.

Local mitigation (documented, not a fix)

A plugin hooking experimental.compaction.autocontinue and denying the synthetic turn when the recent history shows threshold identical completed tool calls across messages breaks the loop mechanically; ships in our config repo. But the upstream fixed point (compaction that cannot shrink below usable() still re-firing forever) is the real bug.

Happy to provide the full event-store dump (session ses_f06ae22e5ffeFv7pyZePkenY5i, seq 190–676) if useful.

Activity

  1. github-actions commented on Oct 2, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • Please use the Bug Report issue template and include its required Description field; the current body uses custom headings rather than the issue-template fields.
    • Please add relevant system information (for example, your operating system) to help reproduce the issue; the OpenCode version and model are already included.

    Please edit this issue to address the above within 72 hours, or it will be automatically closed.


    This issue might be a duplicate of existing issues. Please check:

    • #48827: Auto-compaction loops when retained context exceeds the usable window, creating a similar endless compact/continue cycle.
    • #27924: Infinite compaction loop when compression fails to reduce context.
    • #25254: Doom-loop detection misses repeated tool calls across assistant messages.
    • #15533: Auto-compaction synthetic continuation can drive repeated compaction/continue cycles.

    If you believe this was flagged incorrectly, please let a maintainer know.

  2. github-actions commented on Oct 5, 2026

    @github-actions
    Contributor

    This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 72-hour window.

    Feel free to open a new issue that follows our issue templates.

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

Metadata

Metadata

Assignees

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