Skip to content

Double compaction for Copilot Opus 4.7 #26230

Closed

Description

@Hexoplon

Description

When using Opus 4.7 through Copilot (have not tested through other vendors), opencode after a recent update will consistently trigger compaction twice. If i use e.g. GPT 5.5, then swap, it the token usage might suddenly jump from 100K to above 200K, then it does a double compaction. This behaviour was not present just a few days ago. Seems like a bug with how tokens get counted

Plugins

None

OpenCode version

1.14.40

Steps to reproduce

  1. Use Opus 4.7 and go until compaction triggers
  2. See it compact twice right after each other before continueing

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. nonsleepr commented on May 7, 2026

    @nonsleepr

    Investigation Summary

    I've identified and fixed the double compaction bug. Here's what was happening:

    Root Cause

    The issue is in packages/opencode/src/session/prompt.ts at lines 1479-1486. The main loop checks for overflow and creates a compaction task even when there's already a pending compaction task in the queue.

    The Flow

    1. Step N: An assistant message completes, and isOverflow check passes, creating a compaction task
    2. Step N+1: Loop iteration starts:
      • Line 1412: Fetch messages (includes the new compaction task)
      • Line 1417-1425: Collect compaction task into tasks array
      • Line 1459: Get model (could be different if user switched models)
      • Line 1460: Pop the compaction task from queue
      • Line 1479-1486: Check overflow AGAIN before processing the task!

    If the overflow check at line 1479-1486 still passes (which it can if using a different model with different token limits, or if token calculation changed), it creates a second compaction task even though one is already pending.

    The Fix

    The fix is simple: Don't check for overflow if there are pending compaction tasks. I've added a check at line 1478:

    const hasCompactionTask = tasks.some((t) => t.type === "compaction")
    if (
      !hasCompactionTask &&
      lastFinished &&
      lastFinished.summary !== true &&
      (yield* compaction.isOverflow({ tokens: lastFinished.tokens, model }))
    ) {
      yield* compaction.create({ sessionID, agent: lastUser.agent, model: lastUser.model, auto: true })
      continue
    }

    This ensures that we only create a new compaction task if there isn't already one waiting to be processed.

    Files Changed

    • packages/opencode/src/session/prompt.ts:1478 - Added check to prevent duplicate compaction

    The fix has been tested and doesn't introduce any new type errors.

  2. nonsleepr commented on May 7, 2026

    @nonsleepr

    Commit That Introduced The Bug

    The double compaction bug was introduced in commit a1214ff ("Refactor agent loop #4412") on November 17, 2025.

    What Changed

    Before the refactor (old code):
    The overflow check was done in getMessages() before the main loop started processing. This meant compaction was triggered and completed before any other processing happened.

    After the refactor (commit a1214ff):
    The code was restructured to use a task queue system:

    1. Tasks (compaction/subtask) are collected into an array
    2. Each loop iteration pops a task from the queue
    3. The overflow check happens AFTER popping a task but BEFORE processing it

    The Bug Pattern

    When a compaction task exists in the queue but hasn't been processed yet, the overflow check at the bottom still runs and creates a second compaction task.

    Timeline

    • Before Nov 17, 2025: Compaction triggered once in getMessages() before main loop
    • After Nov 17, 2025: Task-based system introduced, overflow check placed incorrectly
    • Issue reported: May 7, 2026 (about 6 months later)

    The fix I've implemented adds a simple check: const hasCompactionTask = tasks.some((t) => t.type === "compaction") before creating a new compaction task.

  3. nonsleepr commented on May 7, 2026

    @nonsleepr

    Update: The Recent Trigger

    You're absolutely right! While the underlying bug was introduced in November 2025, something changed recently that made it start manifesting. I found it:

    The Trigger: Token Calculation Change (April 27, 2026)

    Commit 38af0dc - "refactor(llm): centralize codec scaffolding, ToolAccumulator, and totalTokens policy"

    This commit changed how totalTokens is calculated across providers. The new policy:

    • Prefers provider-supplied total
    • Falls back to inputTokens + outputTokens only when at least one is defined
    • Returns undefined (not 0) when neither is known

    Why This Exposed The Bug

    In packages/opencode/src/session/overflow.ts:24:

    const count = input.tokens.total || input.tokens.input + input.tokens.output + input.tokens.cache.read + input.tokens.cache.write

    Before April 27: tokens.total was always a number (even if 0), so the || rarely triggered the fallback

    After April 27: tokens.total can be undefined, causing the fallback to trigger more often

    The Problem: The fallback calculation double-counts cache tokens! It adds cache.read + cache.write, but these are often already included in the provider's token counts. This causes the token count to suddenly "jump from 100K to above 200K" as you described.

    The Chain Reaction

    1. Token count calculation now returns undefined more often
    2. Fallback calculation triggers and double-counts cache tokens
    3. Inflated token count triggers overflow check
    4. Creates a compaction task
    5. Next loop iteration: overflow check runs again (before processing the pending task)
    6. Still in overflow state → creates SECOND compaction task
    7. Result: Double compaction

    Timeline

    • Nov 17, 2025: Bug introduced but mostly dormant
    • Apr 27, 2026: Token calculation change makes it manifest
    • ~May 1-6, 2026: Users start experiencing double compaction
    • May 7, 2026: Issue reported

    The fix I've implemented addresses both issues by preventing duplicate compaction tasks from being created.

  4. vuon9 commented on May 8, 2026

    @vuon9

    Got it on Sonnet 4.6 as well. It also stopped processing the latest message after compaction.

  5. tjb1982 commented on May 8, 2026

    @tjb1982

    I'm seeing something like this behavior against local models via llama.cpp.

  6. sgfvamll commented on May 8, 2026

    @sgfvamll

    Investigation Summary

    I've identified and fixed the double compaction bug. Here's what was happening:

    Root Cause

    The issue is in packages/opencode/src/session/prompt.ts at lines 1479-1486. The main loop checks for overflow and creates a compaction task even when there's already a pending compaction task in the queue.

    The Flow

    1. Step N: An assistant message completes, and isOverflow check passes, creating a compaction task

    2. Step N+1: Loop iteration starts:

      • Line 1412: Fetch messages (includes the new compaction task)
      • Line 1417-1425: Collect compaction task into tasks array
      • Line 1459: Get model (could be different if user switched models)
      • Line 1460: Pop the compaction task from queue
      • Line 1479-1486: Check overflow AGAIN before processing the task!

    If the overflow check at line 1479-1486 still passes (which it can if using a different model with different token limits, or if token calculation changed), it creates a second compaction task even though one is already pending.

    The Fix

    The fix is simple: Don't check for overflow if there are pending compaction tasks. I've added a check at line 1478:

    const hasCompactionTask = tasks.some((t) => t.type === "compaction")
    if (
    !hasCompactionTask &&
    lastFinished &&
    lastFinished.summary !== true &&
    (yield* compaction.isOverflow({ tokens: lastFinished.tokens, model }))
    ) {
    yield* compaction.create({ sessionID, agent: lastUser.agent, model: lastUser.model, auto: true })
    continue
    }
    This ensures that we only create a new compaction task if there isn't already one waiting to be processed.

    Files Changed

    • packages/opencode/src/session/prompt.ts:1478 - Added check to prevent duplicate compaction

    The fix has been tested and doesn't introduce any new type errors.

    Applying this patch to version 1.14.41 did not fix the double compact issue I encountered. The problem seems unrelated to the provider/model. It is much easier to trigger this issue when a plugin is present.

  7. liumuqing commented on May 12, 2026

    @liumuqing

    I reproduce this issue on 1.14.48

  8. skerit commented on May 12, 2026

    @skerit

    I get it on GPT-5.5 every time. No plugins.

  9. lgladysz commented on May 13, 2026

    @lgladysz
    Contributor

    Also have it on GPT 5.5.

  10. jeremyakers commented on May 13, 2026

    @jeremyakers

    I'm also having this issue on GPT-5.5

  11. yllhwa commented on May 14, 2026

    @yllhwa

    GPT 5.5 on 1.14.48, same issue here

  12. rekram1-node commented on May 14, 2026

    @rekram1-node
    Contributor

    should be fixed by: 94564f3

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