anomalyco / anomalyco/opencode

Failed compaction silently drops pre-tail history from the model context

Open
#42,282 0 comments 0 reactions 1 assignee View on GitHub

@kitlangton is already working on this.

Since Aug 13, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Description

When a compaction summary turn fails with a provider error, everything before the compaction boundary is dropped from the model context and no summary replaces it. The messages stay in the database, so the UI looks fine — the model just stops knowing about the earlier turns, and it repeats on every following turn because the marker is already persisted.

Two things combine:

  1. processCompaction persists tail_start_id on the compaction part even when the summary turn failed. The early return only covers result === "compact" (packages/opencode/src/session/compaction.ts:450-466); a provider error returns "stop" and falls straight through to the write.
  2. filterCompacted uses two different tests for "this compaction is complete". The truncation loop requires summary && finish && !error (message-v2.ts:541-542), but the reordering branch below it only checks summary (message-v2.ts:554-562). That branch returns [compaction..summary] + [tail..compaction] + [after summary], so everything before tail_start_id is dropped. With a failed summary there is nothing to replace it, and the errored assistant message is itself dropped by toModelMessagesEffect.

So the loop says "not complete, keep all history" while the reorder says "complete, drop everything before the tail". The result is neither the summary nor the history.

This is the specs/v2/session.md rule ("A failed or interrupted attempt therefore leaves the previous history boundary active") not holding in v1.

Not #30806 (dangling partial summary on abort, closed) or #24700 / #24781 (fork not remapping tail_start_id). #42063 is adjacent but complementary — it rejects empty summaries, and its guard is result === "continue" && !processor.message.error, so the errored path here never reaches it.

Disclosure: found and reproduced with AI assistance; I reviewed and verified every claim locally.

Plugins

No response

OpenCode version

dev @ cc4b456 (v1.18.18)

Steps to reproduce

Happens whenever the summary request fails in a non-retryable way — expired key, exhausted credits, model unavailable, content filter — which is likely exactly when the context is largest.

Reproduced end to end against the real SessionCompaction.process with persistence: three turns, then an auto-compaction whose summary request returns 401.

Before: process() returns stop, the summary message carries the API error with no finish, but tail_start_id is still written. The next turn sees 3 of 5 stored messages — the first two topics are gone.

After adding finish / !error to the reordering test, the next turn sees all 5.

Screenshot and/or share link

No response

Operating System

macOS 15

Terminal

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.