Skip to content

Session compaction failed #54797

Closed
Closed

Description

@ppereirasky

Description

Hi There,

First of all - HUGE thumbs up for this project. I have been using it daily in long horizon tasks with success.
During my last long run (aprox 4 hours) this issue happened 3 times - (it has also happened before with all previous desktop versions)

This model's maximum context length is 139264 tokens. However, you requested 63472 output tokens and your prompt contains at least 75793 input tokens, for a total of at least 139265 tokens. Please reduce the length of the input prompt or the number of requested output tokens. (parameter=input_tokens, value=75793)

It's like it needed 1 more token that it didn't have available to use. It happens on any context value - always by +1 token more needed. Usually I retry and it recovers - (compacts and keep going) but it needs my manual intervention of course.

Plugins

I'm using superpowers

OpenCode version

Desktop 2.0.27 - but it happens since version 1 - when I started using the desktop version

Steps to reproduce

Long horizon task, on complex project, on perhaps a smaller context to increase the number of times it needs to compact - it will happen no doubt.

Screenshot and/or share link

Image

Operating System

Windows 10

Terminal

Desktop Opencode

Activity

  1. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    Thanks for the kind words and the clear report!

    One note first: the "+1 token" is probably not a real off-by-one. That wording (your prompt contains at least … (parameter=input_tokens, value=…)) comes from vLLM-style backends, which report a lower bound for the prompt: the context limit minus the requested output, plus 1. That means the real prompt was probably bigger than OpenCode estimated, and the request asked for more output than was actually left in the window.

    To reproduce this, could you share:

    1. The provider and model you were using (e.g. provider/model as shown in the model picker). Is it a built-in provider or a custom OpenAI-compatible / vLLM endpoint?
    2. Any model config that affects limits: a limit (context / output) block, maxTokens or similar options, or a selected variant, from your opencode.json. Please remove any API keys.
    3. Which request fails: does the error show on the compaction step itself ("Session compaction failed"), or on a normal reply just before compaction? In 2.0.27 a compaction summary asks for at most 32,000 output tokens, so a request for 63,472 suggests either a normal turn or a setting or plugin (e.g. superpowers) changing the output limit.

    You can reply here with the details, or edit the issue. If you edit it, please also leave a comment saying you've updated it, so we know when you're done.

    These issues look related. None is clearly the same problem; they're ordered from most to least likely:

  2. ppereirasky commented on Oct 12, 2026

    @ppereirasky
    Author

    Hi There,

    Thanks for the fast reply.

    Image
  3. opencode-agent commented on Oct 12, 2026

    @opencode-agent
    Contributor

    Thanks for the details. I was able to reproduce this on 2.0.27, and it still happens on the latest dev (bcf23b677).

    I used a mock server that behaves like vLLM: a 139,264-token limit, a tokenizer that counts dense code more heavily than OpenCode's estimate does, and vLLM's exact error text. OpenCode used your provider config (context: 131072, output: 65536). I sent one long, dense prompt in a fresh session. OpenCode asked for 38,890 output tokens on a prompt that was really about 112.5k tokens and got the same "total of at least 139265 tokens" error. The compaction that followed was rejected too. The script below sets this up end to end. The mock's tokenizer is my approximation, not Qwen's real one.

    What's going on:

    • When OpenCode has no token count reported by the provider, it estimates the prompt size at about 4 characters per token plus a 15% margin. That's the case on the first turn of a session and on the turn right after a compaction, which matches what you see. It then asks for all the remaining room as output.
    • On dense code, or a tokenizer that produces more tokens than that estimate, the real prompt is bigger, so the request goes over the limit.
    • The "+1" comes from how vLLM reports the error: it gives the smallest prompt size that would overflow, so the total always shows as the limit plus one. The actual overflow can be much larger.

    Possible workaround (I haven't checked it against your setup): a lower limit.output (e.g. 32768) leaves more headroom and should make this happen less often.

    Related: #38108 (same vLLM error shape), #50187, #54690.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Repro for #54797: first request of a turn with no provider-measured anchor (as after compaction) sizes max_tokens
    # from a chars/4 estimate; a vLLM server with a denser real tokenization rejects it by "+1 token".
    # Starts a mock vLLM server (max_model_len 139264, ~3 chars/token) on :8765 and logs each request's max_tokens/prompt size.
    set -e
    cd "$(dirname "$0")"
    cat > mock-vllm.ts <<'TS'
    // Mock vLLM OpenAI-compatible server: max_model_len 139264, ~3 chars/token (dense code-like text).
    const MAX = 139264
    Bun.serve({
      port: 8765,
      async fetch(req) {
        const url = new URL(req.url)
        if (url.pathname.endsWith("/models")) return Response.json({ object: "list", data: [{ id: "qwen", object: "model" }] })
        const body: any = await req.json()
        const chars = JSON.stringify(body.messages).length + JSON.stringify(body.tools ?? []).length
        const prompt = Math.ceil(chars / 3)
        const maxTokens = body.max_tokens ?? body.max_completion_tokens
        console.log(JSON.stringify({ path: url.pathname, max_tokens: maxTokens, prompt_tokens: prompt, total: prompt + (maxTokens ?? 0) }))
        if (maxTokens && prompt + maxTokens > MAX) {
          const atLeast = MAX - maxTokens + 1
          return Response.json({ object: "error", type: "BadRequestError", code: 400, param: "input_tokens",
            message: `This model's maximum context length is ${MAX} tokens. However, you requested ${maxTokens} output tokens and your prompt contains at least ${atLeast} input tokens, for a total of at least ${MAX + 1} tokens. Please reduce the length of the input prompt or the number of requested output tokens. (parameter=input_tokens, value=${atLeast})` }, { status: 400 })
        }
        const enc = new TextEncoder()
        const chunk = (o: any) => enc.encode(`data: ${JSON.stringify(o)}\n\n`)
        const base = { id: "c1", object: "chat.completion.chunk", created: 0, model: "qwen" }
        return new Response(new ReadableStream({ start(c) {
          c.enqueue(chunk({ ...base, choices: [{ index: 0, delta: { role: "assistant", content: "OK, done." }, finish_reason: null }] }))
          c.enqueue(chunk({ ...base, choices: [{ index: 0, delta: {}, finish_reason: "stop" }], usage: { prompt_tokens: prompt, completion_tokens: 3, total_tokens: prompt + 3 } }))
          c.enqueue(enc.encode("data: [DONE]\n\n")); c.close()
        } }), { headers: { "content-type": "text/event-stream" } })
      },
    })
    console.log("mock vllm on 8765")
    TS
    bun mock-vllm.ts > mock.log 2>&1 & MOCK=$!; trap 'kill $MOCK' EXIT; sleep 1
    OC=${OPENCODE:-opencode}
    export HOME=$(mktemp -d) XDG_CONFIG_HOME= XDG_DATA_HOME= XDG_STATE_HOME= XDG_CACHE_HOME=
    mkdir -p proj && cd proj
    cat > opencode.jsonc <<'JSON'
    {
      "provider": {
        "vllm": {
          "npm": "@ai-sdk/openai-compatible",
          "name": "Local vLLM",
          "options": { "baseURL": "http://127.0.0.1:8765/v1", "apiKey": "x" },
          "models": { "qwen": { "name": "Qwen3.8-27B", "limit": { "context": 131072, "output": 65536 } } }
        }
      },
      "model": "vllm/qwen"
    }
    JSON
    git init -q 2>/dev/null || true
    # ~300k chars of dense code-like text (an estimate of ~75k tokens at 4 chars/token)
    python3 - <<'PY'
    import random; random.seed(1)
    lines=[]
    while sum(len(l)+1 for l in lines) < 300000:
        lines.append(f"x{random.randint(0,9999)}=f({random.randint(0,99)},[{random.randint(0,9)},{random.randint(0,9)}]);")
    open("dense.txt","w").write("\n".join(lines))
    PY
    $OC run --standalone -m vllm/qwen -f dense.txt "Summarize this file." 2>&1 | tail -15
    echo "--- requests seen by the mock server:"; grep path ../mock.log

    Run it: bash repro.sh

  4. ppereirasky commented on Oct 12, 2026

    @ppereirasky
    Author

    OK - I will lower the output limit to 32768 and keep going to see how it behaves.

    Thanks a lot for the help trying to debug and solve this and for the blazing fast replies. GREAT work.
    👍

  5. ppereirasky commented on Oct 12, 2026

    @ppereirasky
    Author

    Just to let you know - before I changed anything this also happened (see picture)

    Image

    It has never happened before - should I lower the output limit despite this error message?

    I retried and it kept going:

    Image
  6. self-assigned this
    on Oct 15, 2026
  7. rekram1-node commented on Oct 15, 2026

    @rekram1-node
    Collaborator

    lookin

  8. rekram1-node commented on Nov 3, 2026

    @rekram1-node
    Collaborator

    This should be fixed after some recent durability cahnges we made in last few releases

  9. ppereirasky commented on Nov 4, 2026

    @ppereirasky
    Author

    Justo to let you know that even with the last version of the Desktop App this is still happening.
    I'm currently running several long horizon tasks with a Qwen 3.8 27B backend and this happens at least 5 or 6 times a day - once for each project.

    Image

    max context on vllm for Qwen is 118784 and I set opencode.jsonc to:
    "limit": {
    "context": 118784,
    "output": 59392
    },

    at least one time a day - even when I try chatting 'retry' several times it doesn't work. The only solution is go back to my last prompt before the failure and restart from there.

    Regarding the above image - that happened just now - I tried prompting 'retry' and it worked. 'retry' prompt works around 80% of the time.

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions