Skip to content

opencode-go: long sessions stuck at provider input limit; auto-compaction never triggers #56296

Description

@z99733543-cell

Description

Using opencode-go/deepseek-v4.1-flash in one long session, once the accumulated prompt reached ~270k tokens, every request was rejected by the upstream gateway with:

400 [unsupported_request] This text request is too large for the model's configured input limit.

The session cannot recover on its own:

  • /api/model (provider opencode-go) advertises limit.context = 1000000 for this model, so automatic compaction never runs before the gateway's actual input limit (~just above 270k) is reached.
  • The documented "recover once when a provider rejects a request as too long" behavior (v2 compaction docs) does not trigger: the message history contains zero compaction messages after the first rejection, and three consecutive attempts in ~2 minutes all failed identically. Server log shows repeated Failed to drain Session with the same AI.Error.InvalidRequest.
  • Even trivial prompts fail, because the full history is resent every time; the session stays permanently stuck (outcome failed).
  • Workaround that works: manual opencode api post /api/session/{sessionID}/compact --data '{}' — the summary is generated by the same model/endpoint and completes, after which the session is usable again (outgoing context payload shrank ~7x).

Plugins

None. Config only sets "shell": "powershell"; no plugin entries or plugin files.

OpenCode version

2.0.32 — opencode --version prints opencode v2.0.32 (OpenCode Desktop, bundled CLI 2.0.32-x64).

Steps to reproduce

  1. Use opencode-go/deepseek-v4.1-flash in one session through a long stretch of heavy tool use until the request payload approaches ~270k tokens. Last successful request observed: 270,421 tokens (11,070 input + 259,351 cache read; prompt caching active).
  2. Send any next prompt.
  3. Every request now fails with AI.Error.InvalidRequest: Upstream request failed: [unsupported_request] This text request is too large for the model's configured input limit. (HTTP 400). No session.compaction.* activity and no compaction message is ever created; auto-recovery never runs, so the session is stuck.
  4. Manual POST /api/session/{sessionID}/compact --data '{}' succeeds and unblocks the session.

OS

Windows 10.0.26300.9711 (win32 x64)

Terminal

Not a terminal — OpenCode Desktop app.

Additional context

Expected: either auto-compaction fires before the provider's actual limit, or a "too large" rejection triggers the documented compact-and-retry-once recovery.

Server log excerpt (redacted paths):

level=ERROR message="Failed to drain Session" cause="AI.Error: Upstream request failed: [unsupported_request] This text request is too large for the model's configured input limit.
    at SessionStep.attempt
    at SessionRunner.runStep
    at SessionRunner.drain }
  [cause]: AI.Error.InvalidRequest: Upstream request failed: [unsupported_request] This text request is too large for the model's configured input limit."

This looks like two separate problems:

  1. Catalog/gateway mismatch: advertised limit.context = 1000000 vs enforced ~270k input limit, which makes the automatic compaction threshold unreachable (default 10% buffer ⇒ ~900k).
  2. The provider's "too long" rejection doesn't appear to be classified for the documented recover-once path.

Manually compacting the same session proves the provider is fine — only the oversized prompt is rejected, and summarization (same model/endpoint) succeeds.

Identifiers, local paths, and account details redacted.

Activity

  1. opencode-agent commented on Oct 17, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed report. I reproduced this on v2.0.32, and it still happens on the latest dev (512165cec).

    To test it, I pointed OpenCode at a mock OpenAI-compatible provider that advertises a 1M context and answers normal requests with HTTP 400 and the gateway's exact message, This text request is too large for the model's configured input limit.. Compaction requests get a successful reply.

    • With that message, opencode run fails with the error, and no compaction request is ever sent.
    • With prompt is too long in its place (a message OpenCode already recognises as context overflow) and everything else the same, OpenCode sends compaction requests right after the 400.

    So the recover-once compaction works, but this gateway's wording isn't on the list of context-overflow errors in packages/ai/src/provider-error.ts, so it never starts. That matches what you saw. The script below reproduces it.

    The other half of your report, the catalog advertising a 1M context while the gateway enforces about 270k, is on the opencode-go side, and a mock can't confirm it. It is tracked in #50446 (same pattern on qwen3.8-flash) and #50761 (route-dependent limits on deepseek-v4.1-flash). Until this is fixed, compacting manually, as you did, is the workaround.

    Reproduction script (repro.sh)
    #!/usr/bin/env bash
    # Repro for #56296: a 400 "This text request is too large for the model's configured input limit."
    # is not treated as context overflow, so no compact-and-retry happens.
    # Usage: ./repro.sh            -> uses `opencode` from PATH
    #        OC_DEV=/path/to/repo ./repro.sh -> runs from source
    # MSG overrides the error message (e.g. MSG="prompt is too long" as a control).
    set -u
    DEFAULT_MSG="This text request is too large for the model's configured input limit."
    MSG="${MSG:-$DEFAULT_MSG}"
    W=$(mktemp -d); PORT=${PORT:-18765}
    cat > "$W/mock.ts" <<'TS'
    let n = 0
    const msg = process.env.MSG!
    Bun.serve({ port: Number(process.env.PORT), async fetch(req) {
      const url = new URL(req.url)
      if (url.pathname.endsWith("/models")) return Response.json({ data: [{ id: "flash", object: "model" }] })
      const body: any = await req.json().catch(() => ({}))
      n++
      const text = JSON.stringify(body.messages ?? []).toLowerCase()
      const isSummary = text.includes("into a structured summary") || text.includes("consolidated summary")
      console.error(`[mock] request #${n} messages=${body.messages?.length} summaryRequest=${isSummary}`)
      // Simulate the gateway: the full (oversized) history is rejected; a compaction/summary request succeeds.
      if (!isSummary) return Response.json({ error: { type: "unsupported_request", code: "unsupported_request", message: msg } }, { status: 400 })
      const chunk = (d: any) => `data: ${JSON.stringify({ id: "x", object: "chat.completion.chunk", created: 0, model: "flash", choices: [d] })}\n\n`
      const s = chunk({ index: 0, delta: { role: "assistant", content: "summary ok" } }) + chunk({ index: 0, delta: {}, finish_reason: "stop" }) +
        `data: ${JSON.stringify({ id: "x", object: "chat.completion.chunk", created: 0, model: "flash", choices: [], usage: { prompt_tokens: 10, completion_tokens: 2, total_tokens: 12 } })}\n\ndata: [DONE]\n\n`
      return new Response(s, { headers: { "content-type": "text/event-stream" } })
    } })
    TS
    MSG="$MSG" PORT=$PORT bun "$W/mock.ts" 2> "$W/mock.log" & MP=$!
    sleep 1
    mkdir -p "$W/proj" "$W/home"
    cat > "$W/proj/opencode.json" <<JSON
    { "\$schema": "https://opencode.ai/config.json",
      "provider": { "mock": { "npm": "@ai-sdk/openai-compatible", "name": "Mock gateway",
        "options": { "baseURL": "http://127.0.0.1:$PORT/v1", "apiKey": "x" },
        "models": { "flash": { "name": "flash", "limit": { "context": 1000000, "output": 32000 } } } } } }
    JSON
    cd "$W/proj"
    export HOME="$W/home" XDG_CONFIG_HOME="$W/home/.config" XDG_DATA_HOME="$W/home/.data" XDG_STATE_HOME="$W/home/.state" XDG_CACHE_HOME="$W/home/.cache"
    if [ -n "${OC_DEV:-}" ]; then
      export BUN_OPTIONS="--conditions=browser --preload=$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js"
      OC=(bun --conditions=browser --preload=$OC_DEV/packages/tui/node_modules/@opentui/solid/scripts/preload.js $OC_DEV/packages/cli/src/index.ts)
    else OC=(opencode); fi
    echo "version: $("${OC[@]}" --version)"
    timeout 90 "${OC[@]}" run -m mock/flash "hi" 2>&1 | tail -15
    echo "--- mock log ---"; cat "$W/mock.log"
    kill $MP; pkill -x opencode.exe 2>/dev/null; [ -n "${OC_DEV:-}" ] && pkill -f "$OC_DEV/packages/cli/src/index"; sleep 1

    Run it: bash repro.sh

  2. z99733543-cell commented on Oct 17, 2026

    @z99733543-cell
    Author

    Thanks — the reproduction makes sense (the wording isn't in the context-overflow classification, so the recover-once never starts).

    One more data point from the same machine/time, suggesting the compact-and-retry would be worth extending to provider 400s of this class generally: a second failure mode on the same model wedges sessions identically — if any message/tool-result contains an image/file part (e.g. read on a .png), every subsequent request fails with:

    400 [unsupported_request] Message content supports text content only. Images, audio and files are not enabled.
    

    while the catalog advertises capabilities.input: ["text","image"] for the model. Like the size case, the session only unblocks via manual POST /api/session/{id}/compact (the summary path drops attachments).

    Also, FWIW, the size side looks route-dependent rather than a fixed ~270k cap: the same account/day shows successful requests at 272k–900k tokens on this model (max 899,929; incl. one 884,448 fully uncached), and the two wedging points measured ≈276k and ≈302k prompt tokens — consistent with the known #50761 route-dependent limits.

  3. self-assigned this
    on Oct 20, 2026
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