anomalyco / anomalyco/opencode
Turns truncated at the output limit are recorded as normal completions
@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:36packages/core/src/v1/session.ts:48packages/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
- Use a model whose output limit is reachable in one turn (the 32K
OUTPUT_TOKEN_MAXclamp inpackages/opencode/src/provider/transform.ts:18makes this common). - 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.
- Observe the session end. The stored assistant message has
finish: "length"and no error; ataskdispatch reportsok.
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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.