Skip to content

bug(v2): Fable/Zen request-size 400 bypasses auto-compaction #35013

Description

@kitlangton

Description

OpenCode V2 auto-compaction does not trigger for long Fable sessions that exceed the Zen/Fable request byte envelope before they approach the advertised token context limit.

Observed behavior:

  • Model: opencode/claude-fable-5#high through Zen.
  • Catalog limit for Fable: context=1000000, output=128000.
  • V2 auto-compaction threshold: context - max(output, buffer) = 872000 estimated tokens.
  • A real long session translated to 646 LLM messages and about 1.31 MB of request JSON before system/tools.
  • Token.estimate(JSON.stringify({ system, messages, tools })) for messages-only shape was about 328000, below the 872000 threshold, so SessionCompaction.compactIfNeeded returned false.
  • The provider then failed before any assistant output with HTTP 400:
Provider request failed with HTTP 400: {"type":"error","error":{"type":"invalid_request_error","message":"Error from provider (Console): Upstream request failed"}}

No session.next.compaction.* events were emitted. The turn became session.next.step.failed.2.

Why This Happens

The proactive path in packages/core/src/session/compaction.ts only checks estimated tokens against the advertised model context:

estimate({ system: input.request.system, messages: input.request.messages, tools: input.request.tools }) <=
  context - Math.max(output, config.buffer)

The reactive overflow path in packages/core/src/session/runner/llm.ts only recovers when isContextOverflowFailure(...) is true. The Zen/Fable response body says only Upstream request failed, so packages/llm/src/provider-error.ts does not classify it as context overflow.

Synthetic Reproduction

Against https://opencode.ai/zen/v1/messages with model=claude-fable-5, max_tokens=1, and no private content:

  • 700000 characters in one user text message succeeds.
  • 1000000 characters in one user text message returns the same generic HTTP 400 Upstream request failed.
  • 650 tiny alternating messages ending in a user turn succeeds, so this is not simply message count.
  • Small Anthropic tool-use/tool-result shapes also succeed, so this is not obviously invalid tool-result ordering.

Expected Behavior

V2 should compact before sending requests that are too large for the route/provider envelope, or classify this generic Zen/Fable 400 as recoverable overflow when it happens before assistant output.

Possible fixes:

  • Add a byte-envelope guard to SessionCompaction.compactIfNeeded, perhaps route/model configurable and separate from token context.
  • Add provider/route metadata for max request bytes or a lower effective compaction threshold for Zen/Fable.
  • Improve Zen/Fable error classification so this generic request-size failure becomes context-overflow when appropriate.

Related Issues

Related but not the same:

This report is specifically about V2 packages/core/src/session/compaction.ts using token context only while Zen/Fable appears to fail at a smaller request-byte envelope.

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.0bugSomething isn't workingcoreAnything pertaining to core functionality of the application (opencode server stuff)

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions