Skip to content

Bug: opencode run exits after compaction when compaction model's token usage exceeds overflow threshold #13946

Closed as not planned
gignit/opencode
#3

Description

@andrea-tomassi

Description

When using opencode run (non-interactive/headless mode), the process exits cleanly (exit code 0) after auto-compaction if the compaction model's own response has total token usage exceeding the model's overflow threshold.

This happens because the compaction processor's finish-step handler runs the same isOverflow() check on the compaction model's token usage. When the compaction input is large (>~94k tokens for a model with 128k context/32k output), isOverflow() returns true, causing processor.process() to return "compact" instead of "continue". This prevents the synthetic "Continue..." user message from being created, which in turn causes the prompt loop's exit condition to fire.

Steps to Reproduce

  1. Start a session with opencode run
  2. Accumulate context through tool calls (file reads, etc.) until tokens reach ~80-95k
  3. Read several large files in rapid succession, pushing context to ~100k+ tokens in a single turn
  4. Auto-compaction triggers
  5. The compaction model summarizes successfully (finish: "stop", no error)
  6. Process exits with code 0 instead of continuing

Root Cause

In packages/opencode/src/session/compaction.ts, the process() function:

const result = await processor.process({ ... })

if (result === "continue" && input.auto) {
  // Create synthetic "Continue..." user message  ← THIS IS SKIPPED
}
if (processor.message.error) return "stop"
return "continue"

When the compaction model's token usage exceeds the overflow threshold, processor.process() returns "compact" (not "continue"). The condition result === "continue" && input.auto is false, so the synthetic "Continue if you have next steps..." user message is never created.

Back in prompt.ts, the message history ends with:

[user: compaction trigger] → [assistant: compaction summary, finish="stop"]

The exit check evaluates:

if (
  lastAssistant?.finish &&                              // "stop" → truthy ✓
  !["tool-calls", "unknown"].includes(lastAssistant.finish) && // true ✓
  lastUser.id < lastAssistant.id                        // compaction trigger < summary ✓
) {
  break  // ← Process exits here
}

Evidence

Analyzed 82 compaction events across 20+ sessions:

Compaction Total Tokens Outcome Count
< 96k ✅ SURVIVED 71
≥ 96k ❌ DIED 11

100% correlation. The threshold matches the isOverflow() calculation: 128k context - 32k maxOutput ≈ 96k usable.

Edge cases:

  • 94,361 input + 655 output = ~95k < 96k → SURVIVED ✅
  • 93,987 input + 2,983 output = ~97k > 96k → DIED ❌

All SURVIVED compactions had the synthetic "Continue..." user message. All DIED compactions did not.

Suggested Fix

Option A: Handle "compact" result in compaction process (minimal)

- if (result === "continue" && input.auto) {
+ if ((result === "continue" || result === "compact") && input.auto) {

Option B: Skip isOverflow check during compaction (recommended)

In processor.ts finish-step handler:

  if (
+   !input.assistantMessage.summary &&
    await SessionCompaction.isOverflow({ tokens: usage.tokens, model: input.model })
  ) {
    needsCompaction = true
  }

This prevents the compaction model's own response from triggering another compaction cycle.

Environment

  • OpenCode version: latest (as of Feb 2026)
  • Mode: opencode run (headless/non-interactive)
  • Models affected: claude-opus-4.6, gemini-3-pro-preview via github-copilot provider
  • OS: Ubuntu Linux 24.04

Related Code

  • packages/opencode/src/session/compaction.ts — process(), isOverflow()
  • packages/opencode/src/session/processor.ts — finish-step handler
  • packages/opencode/src/session/prompt.ts — main loop exit conditions

Activity

  1. added
    coreAnything pertaining to core functionality of the application (opencode server stuff)
    on Feb 17, 2026
  2. andrea-tomassi commented on Feb 17, 2026

    @andrea-tomassi
    Author

    Related Issues

    This appears to be the root cause for several reported compaction issues:

    All describe the same symptom: the agent stops processing after compaction. Our analysis shows this happens specifically when the compaction model's own response triggers isOverflow(), preventing the synthetic "Continue..." user message from being created.

    The key difference from what other reporters observe (TUI "pausing" vs opencode run exiting):

    • In TUI mode, the loop may not exit because the user can manually type "continue" — but the agent still stops because it never received the synthetic prompt
    • In opencode run mode, the loop exits cleanly (code 0) because no user is present to intervene
  3. added a commit that references this issue on Feb 19, 2026
  4. added 3 commits that reference this issue on Feb 25, 2026
    4897385
    7fbb72c
  5. added 2 commits that reference this issue on Feb 26, 2026
  6. Henry-G-H-Huang commented on Mar 3, 2026

    @Henry-G-H-Huang

    any update about this bug?

  7. github-actions commented on May 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  8. johncoffee715 commented on Jul 15, 2026

    @johncoffee715

    Proposed Fix: Allow Continue after compaction overflow

    PR: https://github.com/johncoffee715/opencode/pull/1

    Problem

    When the compaction model's own response exceeds the overflow threshold, the session returned 'stop' and the user saw no feedback. The session just exited after compaction.

    Solution

    In compaction.ts, when result === 'compact' AND auto-compaction is enabled, we now return 'continue' instead of 'stop'. This allows the synthetic 'Continue...' message to be created, so the user can see what happened and decide next steps.

    Before

    Compaction overflow -> session stops silently

    After

    Compaction overflow -> session continues with error message + Continue prompt

    All 52 compaction tests pass.

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

Metadata

Metadata

Assignees

Labels

coreAnything pertaining to core functionality of the application (opencode server stuff)

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions