Skip to content

Server-reported finish_details (e.g. repetition_loop) is not surfaced — loop-guard cuts look like clean stops in UI, DB, and model history #53489

Closed as not planned

Description

@luoming319-cloud

Summary

When a local server cuts a stuck reply with its repetition/loop guard, it reports the reason honestly in finish_details: {"type": "repetition_loop"} (alongside finish_reason: "stop"). opencode reads only finish_reason and does not surface finish_details anywhere (not in the UI, not in the persisted message data — [inferred] dropped at parse time). The turn is then persisted as if it ended normally.

Consequence: everyone downstream is blind to what happened.

Environment

  • opencode CLI (latest, macOS)
  • Model served by mlx-serve 26.10.1 (OpenAI-compatible chat completions). Its loop guard cuts a stuck generation and returns finish_reason: "stop" + finish_details: {"type": "repetition_loop"} — this is documented in the engine's docs and log line ([loop-stop] ... details=repetition_loop).
  • The same field exists in other ecosystems under different spellings (e.g. llama.cpp-style servers report loop cuts distinctly too), so this is not mlx-serve-specific.

Reproduction (observed on our setup)

  1. Agent session, greedy decoding (temperature 0), long agentic tool chain.
  2. The model's thinking block falls into a degenerate cycle (a ~8-token Chinese phrase repeated 10+ times). The server's loop guard cuts it at ~4900 generated tokens and returns finish_details: {"type": "repetition_loop"}.
  3. In opencode's UI: the turn just ends after a wall of repeated text — no notice, no warning, looks like a normal (if weird) reply.
  4. In opencode's session database (message.data), the turn is stored as finish: "stop" — the finish_details field is nowhere. A follow-up turn by the model reading its own history therefore "sees" a normally-completed turn, and (as happened to us) the model confabulates that it recovered on its own.

Expected

opencode should surface loop-guard cuts instead of swallowing them:

  1. At minimum, persist finish_details (or a normalized flag) on the assistant message, and show the user a notice like "reply stopped: repetition loop detected by server" — distinct from "output truncated" and from a clean stop.
  2. Optionally, auto-retry the turn once (a loop cut is a transient sampling failure, not a content decision), similar to existing truncation handling.

Actual

finish_details never reaches the UI, the database, or what the model sees on the next turn — the turn is indistinguishable from a normal completion everywhere downstream.

Why it matters

The three parties involved each hold one piece of the truth and none of them get it:

  • the server knows (it cut the turn and said why);
  • the user sees a mysterious wall of repeated text with no explanation;
  • the model, on the next turn, reads a history entry claiming a clean stop — and will confabulate a narrative about what happened (ours literally reported "I recovered from the loop by sampling noise", which was false: the turn was beheaded by the token limit / loop guard).

This is a small parsing gap with outsized diagnostic value for local-model agent debugging.

Evidence

  • server log: [loop-stop] degenerate tail loop cut after 4900 generated tokens (finish_reason=stop details=repetition_loop tier=long_cycle trim_start=3925)
  • opencode db (message table) for the same turn: finish = "stop", no trace of the loop detail.
  • engine doc: "a reply cut short because the model went in circles reports finish_details: {"type": "repetition_loop"} beside finish_reason."

Activity

  1. github-actions commented on Oct 6, 2026

    @github-actions
    Contributor

    This issue doesn't fully meet our contributing guidelines.

    What needs to be fixed:

    • Please use the Bug Report template's required Description field. The report has substantive content under Summary, but does not include the required Description field.

    Please edit this issue to address the above within 72 hours, or it will be automatically closed.


    This issue might be a duplicate of existing issues. Please check:

    If you believe this was flagged incorrectly, please let a maintainer know.

  2. github-actions commented on Oct 9, 2026

    @github-actions
    Contributor

    This issue has been automatically closed because it was not updated to meet our contributing guidelines within the 72-hour window.

    Feel free to open a new issue that follows our issue templates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions