anomalyco / anomalyco/opencode

Turns truncated at the output limit are recorded as normal completions

Open
#40,146 2 comments 0 reactions 1 assignee View on GitHub

@nexxeln is already working on this.

Since Aug 2, 2026.

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

Description

Description

When a provider stops a turn because it hit the output-token limit, the stream reports finishReason: "length". The session loop treats that as a normal stop.

Two checks in packages/opencode/src/session/prompt.ts exclude only "tool-calls" (and "unknown" in the second) — never "length":

  • :1113 — the entry gate that decides whether a session already has work to do
  • :1295 — the in-loop decision after a provider turn

Nothing anywhere in src branches on "length". The message is stored with finish: "length" and no error, so downstream sees a turn that ended cleanly.

The worst case is a turn that produced nothing usable. A subagent on an OpenAI-compatible gateway spent its entire output budget inside the reasoning channel — the stored parts were step-start, reasoning (129,961 characters), step-finish, with no text and no tool call, and finish: "length". The task completed with status ok and empty output. From the caller's side that is indistinguishable from a subagent that had nothing to say.

MessageOutputLengthError already exists for this case and has no producer:

  • packages/schema/src/v1/session.ts:36
  • packages/core/src/v1/session.ts:48
  • packages/opencode/src/session/message-error.ts:4

It is already a member of the assistant error union, and packages/opencode/src/acp/service.ts:842 already maps it to stopReason: "max_tokens". The plumbing was built for this and never connected to a source.

Steps to reproduce
  1. Use a model whose output limit is reachable in one turn (the 32K OUTPUT_TOKEN_MAX clamp in packages/opencode/src/provider/transform.ts:18 makes this common).
  2. Ask for output large enough to hit the limit — a large file write, or a long reasoning chain on a model that bills reasoning inside the output count.
  3. Observe the session end. The stored assistant message has finish: "length" and no error; a task dispatch reports ok.

Reproducible in a test by queueing a reply with finishReason: "length" against the test LLM server.

OpenCode version

dev @ 1882c33827

Expected

A truncated turn should be distinguishable from a completed one. A turn that produced text or tool calls before truncating is worth continuing; a turn that produced nothing usable should surface an error rather than reporting success with empty output.

Notes

This is item 4 in the root-cause chain of #18108 (P1 in that issue's checklist), filed here on its own because #18108 covers five interacting problems and this one is separable — it needs no change to repairToolCall, the thinking-budget coordination, the doom-loop detector, or OUTPUT_TOKEN_MAX.

Related: #38747 covers the same problem class on the V2 side (streams that end before their protocol completion boundary). #26167 touches the neighbouring code in prompt.ts for empty-stream truncation retries.

I have a fix with tests and will link it here.

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.