Repository navigation
Bug: opencode run exits after compaction when compaction model's token usage exceeds overflow threshold #13946
Description
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
- Start a session with
opencode run - Accumulate context through tool calls (file reads, etc.) until tokens reach ~80-95k
- Read several large files in rapid succession, pushing context to ~100k+ tokens in a single turn
- Auto-compaction triggers
- The compaction model summarizes successfully (finish: "stop", no error)
- 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-previewviagithub-copilotprovider - OS: Ubuntu Linux 24.04
Related Code
packages/opencode/src/session/compaction.ts—process(),isOverflow()packages/opencode/src/session/processor.ts—finish-stephandlerpackages/opencode/src/session/prompt.ts— main loop exit conditions
Activity
andrea-tomassi commentedon Feb 17, 2026
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 runexiting):opencode runmode, the loop exits cleanly (code 0) because no user is present to intervenefix: compaction bugs anomalyco#13946 (exit after compact) and anomaly…
fix(opencode): show MCP tool output in non-interactive run mode
fix(opencode): skip overflow check on compaction summaries
feat(opencode): add double-buffer context management
fix: compaction bugs anomalyco#13946 (exit after compact) and anomaly…
fix: compaction bugs anomalyco#13946 (exit after compact) and anomaly…
Henry-G-H-Huang commentedon Mar 3, 2026
any update about this bug?
github-actions commentedon May 3, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.
fix(session): estimate pending context in isOverflow() and allow Cont…
johncoffee715 commentedon Jul 15, 2026
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.