anomalyco / anomalyco/opencode

opencode-go kimi-k3 intermittently emits raw <|...|> chat-template delimiters and pathological parallel tool-call bursts

Open
#46,411 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

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

Description

Description

Environment

  • opencode build: 0.0.0-dev-202608271917 (and later dev builds) (own fork with own bug fixes and features, however, the issue is not in the opencode client, but upstream in the servers hosting the Kimi K3 model)
  • model: kimi-k3, provider: opencode-go
  • runtime: ai-sdk, llm.runtime=ai-sdk
  • observed over long autonomous "build" runs (multiple hours), context kept small via compaction (~17k–200k tokens)

Summary

On long-running sessions, kimi-k3 (served through opencode-go) periodically produces malformed responses in two ways that both look like the model "glitching," and both seem to stem from the model's thinking→answer→tool-call structure markers being mishandled.

Symptom 1 — raw template delimiters leak into visible output, then the stream truncates

Reasoning parts intermittently end with literal delimiter tokens instead of a real answer or tool call, e.g.:
reasoning| ... via images:<|close|>response<|sep|><|open|>tools<|sep|><|open|>call t…
<|close|>, <|open|>, <|sep|>, plus the response / tools / call markers, appear to be the model's internal separators for thinking → response → tool-call. They leak as raw text and the stream dies mid-token (cut off at call t…). Result: a turn that ends finish="stop" with reasoning but no answer and no tool call — to the client it looks like "the model thought and then just stopped." We've also captured turns that are literally finish="stop", tokens.output=0, tokens.reasoning in the thousands (pure thinking, zero output).

Symptom 2 — pathological burst of identical parallel tool calls

In one assistant message the model emitted 100 identical tool calls (tessera_get_session_id), and in the next message 97 more — same tool, empty input, all in parallel (callID values like tessera_get_session_id_93, _94, …). A total of ~193 calls landed in a ~2-minute window. The model then reportedly self-detected ("I'm doing this 100 times") but relapsed into another burst. One such message carried 100 tool parts; the next 97.

Why this matters

  • Symptom 1 makes long runs silently lose turns (the model appears to halt after thinking).
  • Symptom 2 burns a large number of tokens/context per message and forces the user to add client-side loop detection to survive autonomous runs.

Suspected root cause

Both symptoms are consistent with opencode-go's kimi-k3 adapter (or the upstream served template) not consuming/parsing the model's <|open|>/<|close|>/<|sep|> structured-output delimiters correctly — the tokens leak into the text stream and the generation terminates abnormally (empty finish → "unknown"/"stop", or runaway repeated tool calls).

Requested

  • Confirm whether kimi-k3 over opencode-go is expected to emit these delimiters, and whether the served chat template should be stripping them.
  • Any known fix/workaround (template version, model revision, stop-token config) for kimi-k3 structured output.
Plugins

No response

OpenCode version

not about the opencode software version, it's about your servers

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

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.

Research direction

The report names no repository file, test, or entry point and attributes the behavior to opencode-go servers hosting kimi-k3 rather than the client. Start by determining whether the adapter or served chat template owns delimiter parsing, using the captured raw delimiters and repeated tool-call messages. Done means confirming ownership and documenting a verified fix or workaround.

Written by the indexing model from the issue text.

Assessment

Domain
ai
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.