Repository navigation
Bug: Assistant messages invisible in web UI when client clock is ahead of server #23092
Description
Description
Assistant messages disappear in the web UI when the client browser clock is ahead of the server clock. The TUI renders the same session correctly.
Root cause: The browser generates user message IDs using local Date.now() for optimistic updates (packages/app/src/components/prompt-input/submit.ts:489). The server reuses this client-generated ID (packages/opencode/src/session/prompt.ts:944). When the client clock is ahead, the user message ID sorts after the first assistant message ID, and the web UI's forward-only positional scan in session-turn.tsx skips it.
The fundamental issue is that the server trusts client-generated time-based IDs, which are not monotonically ordered when clocks diverge.
Reproducible example
A session with 3124ms client clock skew produced this message order (sorted by ID):
[0] Assistant 1: msg_d9b6fff38001... ← sorts BEFORE user message
[1] User message: msg_d9b700b5d001...
[2] Assistant 2: msg_d9b701e9f001...
[3] Assistant 3: msg_d9b704423001...
The assistantMessages memo in packages/ui/src/components/session-turn.tsx scans forward from the user message index only:
for (let i = index + 1; i < messages.length; i++) { // never visits index 0
if (item.role === "user") break
if (item.role === "assistant" && item.parentID === msg.id) result.push(item)
}Assistant 1 at index 0 is never visited → invisible in the UI.
Bug-introducing commit
c0f9b1363 ("fix(desktop): more fine-grained state updates") changed the scan from a full .filter() to a forward-only positional scan, assuming IDs always sort chronologically.
Fix
Replace the forward-only scan with a full-array scan using parentID matching:
- for (let i = index + 1; i < messages.length; i++) {
+ for (let i = 0; i < messages.length; i++) {
const item = messages[i]
if (!item) continue
- if (item.role === "user") break
if (item.role === "assistant" && item.parentID === msg.id) result.push(item as AssistantMessage)This is safe because:
parentIDmatching is the correct invariant, not positional ordering- Revert/fork/retry are unaffected (reverted messages are filtered out before rendering; fork generates fresh IDs)
- Performance impact is negligible (
createMemowithequals: samededuplicates,content-visibility: autoskips off-screen turns)
A more robust fix would also have the server generate all IDs server-side instead of trusting client-generated time-based IDs.
OpenCode version
1.4.8 (still present)
Steps to reproduce
- Run OpenCode server on a machine whose clock is behind the client browser by a few seconds
- Open the web UI and send a message that triggers a multi-step response
- The first assistant message (typically containing reasoning + text + tool call) will not render
- Switch to TUI — all messages display correctly
Related
- Clock skew between client and server breaks prompt loop and message rendering #14236 — same bug report
- Bug: Message ordering broken when client clock is out of sync with server #15657, Message ordering breaks with clock-skewed IDs #17255, Web sessions can loop indefinitely when user message IDs are generated on the client #17012 — clock skew / message ordering issues
- OpenCode web/desktop : no messages displayed when latest 80+ messages in session history are assistant messages #22323 — related pagination issue
- fix: use parentID matching instead of ID ordering for prompt loop exit and message rendering #14307 — open PR with a fix (unmerged since Feb 2026)
Activity
github-actions commentedon Apr 17, 2026
This issue might be a duplicate of existing issues. Please check:
heimoshuiyu commentedon Apr 17, 2026
my out of order msg.json