Skip to content

Opencode infinitely spams messages in a loop and responds to its' messages #49008

Closed as not planned

Description

@nobottomline

Description

I added GLM-5.3-Flash via a custom provider (llmapi.ai). After sending a short test message, the model got stuck in an infinite reply loop, responding to its own text. It would have gone on for hours if I hadn't manually stopped it.

Plugins

No response

OpenCode version

1.18.30

Steps to reproduce

  1. Open opencode
  2. Send a message

Screenshot and/or share link

https://streamable.com/dk3eyr (will expire in 2 days(sep 16))

I can reupload if asked

Operating System

macOS 15.7.3

Terminal

ghostty

Activity

  1. github-actions commented on Sep 14, 2026

    @github-actions
    Contributor

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

    If your issue is distinct from those, please leave a comment explaining how it differs.

  2. nobottomline commented on Sep 14, 2026

    @nobottomline
    Author

    This isn't a duplicate of the linked issues.

    The key thing is what the loop actually does. I send one short message and the model doesn't repeat the same answer, it keeps talking to itself. I write "hi", it answers "hi, how are you?", then another assistant message says "I'm good, and you?", and it just continues that way until I interrupt it. Every message is new and coherent, it's like the assistant is playing both sides of a conversation.

    45533 is not this one: there the model itself breaks and emits endless "!" in reasoning_content (the SGLang bug). Here the text is clean.

    37855 is the closest, but there the same short reply gets regenerated over and over, and only in opencode web. Mine is a progressing conversation and it happens in the TUI.

    26220 is a loop after tool calls, and I'm not calling any tools. 42871 is pure repeated identical requests burning quota, also not what I see.

    My guess is opencode doesn't stop after finish_reason: stop and sends another request where the last message is the assistant's own reply, so the OpenAI-compatible endpoint just continues the assistant turn and the model answers itself. That looks like a loop/termination bug in opencode, not the provider or the model.

    Setup: opencode 1.18.30, macOS 15.7.3, Ghostty (TUI), custom OpenAI-compatible provider on llmapi.ai, model GLM-5.3-Flash. I haven't tested opencode web yet.

    I can post the opencode.json provider block and a debug log if it helps. The Streamable link expires on Sep 16, so I'll reupload the recording here.

  3. Alex668866 commented on Sep 14, 2026

    @Alex668866

    Capture the raw request sequence and finish_reason around the second turn. If OpenCode sends its latest assistant message back without a new user or tool turn, that trace should show whether the loop is in the TUI or at the provider.

  4. nobottomline commented on Sep 14, 2026

    @nobottomline
    Author

    Got the trace. My guess was close, but the trigger is narrower, and it's on the provider side.

    I proxied every request. The first one is normal: roles=[system, user]. After it returns, opencode sends the next one as roles=[system, user, assistant], with no new user message and the assistant's own reply sitting last. Every request after that just appends another assistant message, so the model never gets a fresh user turn and keeps continuing the conversation with itself.

    The reason opencode keeps going is the finish_reason. Every response ends like this:

    data: {"choices":[{"delta":{"content":null},"finish_reason":"tool_calls","index":0}]}

    even when the response contains zero tool calls. Of the 9 responses I captured, only one actually had a tool call (an ls); the other 8 were plain text that still reported finish_reason: "tool_calls". opencode sees "tool_calls", expects tool calls to run, finds none, and re-invokes with the previous assistant message as the last message. The model then just answers itself.

    That also explains why the symptom is a progressing self-dialogue rather than a repeated reply. Nothing is being regenerated, opencode is simply carrying the assistant turn forward.

    So the loop is: provider returns a wrong finish_reason (api.llmapi.ai, model zai-org/GLM-5.3-Flash), and opencode trusts it. This is not #45533, nothing degenerates in reasoning_content. It looks like the same class as #37855 / #26220, just with a different trigger. A guard on "finish_reason says tool_calls but there are no tool calls" would probably stop it on opencode's side.

    I can paste the full request/response sequence if it helps.

  5. Alex668866 commented on Sep 14, 2026

    @Alex668866

    That trace points to an invalid finish_reason: the provider marks plain-text responses as tool_calls, so OpenCode re-enters the assistant turn without a tool result. The provider should fix the field, and OpenCode could stop when tool_calls is set but no calls are present.

  6. amengmon commented on Sep 17, 2026

    @amengmon

    Experiencing the same class of bug on v1.18.31, but with a different finish_reason value. Filing here instead of opening a duplicate because the underlying issue (opencode trusts provider's finish_reason and loops when it's wrong) is identical.

    Setup

    • Provider: custom gateway proxying GLM-5.2, configured with npm: "@ai-sdk/anthropic"
    • Trigger: any user message (even just "你好")

    What happens
    The agent loop never terminates. First ESC aborts the current stream; the loop immediately restarts from step=0. Second ESC finally kills it.

    Hard evidence (queried from opencode.db, session with ~360 assistant turns in one exchange):

    finish count sum(tokens.output)
    unknown 331 0
    tool-calls 10 3189
    "" 11 0

    Sample assistant message record:

    {
      "role": "assistant",
      "agent": "Sisyphus - Ultraworker",
      "cost": 0,
      "tokens": {"input":0,"output":0,"reasoning":0,"cache":{"write":0,"read":0}},
      "modelID": "glm-5.2",
      "providerID": "tal",
      "time": {"created": 1789631840516, "completed": 1789631844256},
      "finish": "unknown"
    }

    How this variant differs from the reporter's case

    • Reporter's case: provider returns finish_reason: "tool_calls" for plain text → opencode expects tool calls, finds none, continues.
    • My case: provider returns finish_reason: "unknown" (or the Anthropic SDK fails to parse a missing/empty stop_reason) with zero output tokens → opencode doesn't see a terminal state and continues.

    Both are "opencode re-invokes because finish_reason doesn't signal end-of-turn" — just with different invalid values.

    Guard suggestion
    The proposed fix for this issue (stop when tool_calls is set but no calls are present) should be broadened to also treat finish === "unknown" combined with tokens.output === 0 as a terminal state. In this case the provider produced no content, so continuing has no useful purpose — the loop can only ever burn more tokens with no output.

    Tried and did not work

    • agent.sisyphus.steps: 1 — ignored, session still ran ~30 steps before first ESC.
    • provider.tal.options.chunkTimeout: 5000 — did not fire; SSE chunks keep arriving with empty content.
    • Disabling runtime-fallback / model-fallback / all todo-continuation-* / background-notification hooks via oh-my-openagent plugin — no effect. This is not a plugin continuation, it's the core agent loop.
  7. Alex668866 commented on Sep 17, 2026

    @Alex668866

    unknown with zero output should break the loop, but OpenCode should show it as a provider/protocol error rather than a normal completed turn. That stops the runaway session without masking malformed or unparseable SSE responses.

  8. thdxr commented on Oct 11, 2026

    @thdxr
    Member

    Thanks for the report! OpenCode 2.0 is now out and development is focused on V2, so we're closing issues filed against OpenCode 1. Please upgrade (opencode upgrade --major, details: https://anoma.ly/notes/opencode-2-0), and if this is still relevant on 2.0, reopen this issue or open a new one with your 2.0 version.

    — from 𝕺𝖕𝖊𝖓𝕮𝖔𝖉𝖊

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