Skip to content

Web: client clock skew can cause repeated assistant responses / prompt loop #28339

Description

@huy-victor

Description

Description

When using the OpenCode web UI from a remote device through a LAN/Tailscale URL, the assistant can repeat the same response multiple times and never return the UI to idle.

The model finishes the response, but the top progress indicator keeps running and the send button remains in the "stop" state. After interrupting, the conversation contains multiple assistant messages responding to the same user message.

This reproduced on a non-host desktop browser via Tailscale. It did not reproduce from the host machine using localhost, and it did not reproduce on my phone.

Environment

  • OpenCode version: 1.15.5
  • OS hosting OpenCode: Windows
  • Web access mode:
    • Host: http://localhost:4096
    • Remote: http://<tailscale-ip>:4096
  • Remote browser: desktop browser on another device
  • Providers/models observed:
    • opencode/minimax-m2.5-free
    • opencode/qwen3.6-plus-free

Steps to reproduce

  1. Run OpenCode web on a host machine.
  2. Open the web UI from another desktop device through a Tailscale or LAN IP.
  3. Send a simple prompt, for example: Hello.
  4. Wait for the assistant to finish.

Actual behavior

  • The assistant answers the prompt.
  • The UI stays busy.
  • The send button does not return.
  • The assistant answers the same user prompt again.
  • This can repeat until the user interrupts the run.
  • The session then shows multiple assistant messages under one user message.

Expected behavior

After a non-tool assistant response finishes with a normal stop reason, the session should become idle and the UI should return the send button.

Suspected root cause

The web client appears to generate the user messageID in the browser before sending the prompt.

From the current source:

  • packages/app/src/components/prompt-input/submit.ts creates a messageID in the web client and passes it to session.promptAsync.
  • packages/app/src/utils/id.ts generates ascending IDs from Date.now().

If the remote browser/device clock is ahead of the OpenCode host clock, the user message ID can sort after the assistant message ID generated on the server. In the installed version I tested, the backend prompt loop appeared to depend on message ID ordering to decide whether the assistant response is after the last user message.

That makes the backend keep prompting even though the assistant already finished normally.

I confirmed this locally by decoding the timestamp-like prefix in the generated IDs from affected sessions: the browser-created user message ID was ahead of the first server-created assistant message ID by about 30 seconds.

As a workaround, I proxied the web UI and patched browser Date.now() to server time before the app loaded. After that, the repeated response loop stopped on the same remote-device setup.

Suggested fix

The backend should not rely on client-generated timestamp IDs for prompt-loop ordering.

Possible approaches:

  • Generate or normalize user message IDs on the server.
  • Compare server-side creation time or explicit parent/turn relationship instead of lexicographic client-generated IDs.
  • Reject or rewrite client message IDs that are ahead of server time by an unsafe margin.

Notes

This issue matters for OpenCode web usage across LAN/Tailscale/multiple devices, where client device clocks are not guaranteed to be perfectly aligned with the host running OpenCode.

Plugins

opencode-gemini-auth@latest @whisperopencode/push @tarquinen/opencode-smart-title

OpenCode version

1.15.5

Steps to reproduce

  1. Run OpenCode web on a host machine.
  2. Open the web UI from another desktop device through a Tailscale or LAN IP.
  3. Send a simple prompt, for example: Hello.
  4. Wait for the assistant to finish.

Screenshot and/or share link

https://opncd.ai/share/iewwIFrS

Operating System

Windows

Terminal

No response

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