openclaw - 💡(How to fix) Fix [Bug]: Webchat user messages disappear due to race condition between chat history reload and transcript persistence [1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#68931Fetched 2026-04-20 12:04:20
View on GitHub
Comments
0
Participants
1
Timeline
1
Reactions
0
Author
Participants
Timeline (top)
subscribed ×1

Error Message

  1. Model API errors/timeout — When the model provider fails (e.g. terminated, timeout), error events can trigger a history reload before the transcript is fully written. Gateway logs confirmed a terminated error on the same run where the message disappeared.
  • Gateway logs confirmed: embedded_run_agent_end with error: "terminated" on the same run where the message disappeared

Root Cause

After analyzing the webchat frontend code and gateway logs, I identified a race condition between the chat history reload function and the transcript persistence:

Fix Action

Fix / Workaround

Fix 1: Merge instead of replace in up()

When loading chat history from the server, preserve locally-added messages that are not yet in the server response. Match by message content + timestamp to avoid duplicates.

Code Example

e.chatMessages = (Array.isArray(i.messages) ? i.messages : []).filter(...)
RAW_BUFFERClick to expand / collapse

Bug Description

User messages can disappear from the OpenClaw webchat UI after being sent. The message is typed, sent, briefly appears, then vanishes. The user did not delete it — the message disappears on its own.

Root Cause Analysis

After analyzing the webchat frontend code and gateway logs, I identified a race condition between the chat history reload function and the transcript persistence:

Message Flow

  1. User types and sends a message
  2. UI immediately adds the user message to chatMessages locally and sets chatSending = true
  3. UI sends chat.send via WebSocket to the gateway
  4. Gateway responds quickly with { runId, status: "started" }
  5. Gateway asynchronously persists the user message to the session transcript via emitUserTranscriptUpdate() — this is non-blocking
  6. Gateway broadcasts a session.message event to connected clients
  7. UI receives the event and calls up() — the chat history loader

The Race Condition

The up() function completely replaces the local chatMessages array with whatever the server returns:

e.chatMessages = (Array.isArray(i.messages) ? i.messages : []).filter(...)

If up() is called before the gateway has finished writing the user message to the transcript, the server returns a history that does NOT include the just-sent message. The local copy (which had the message) is overwritten with the server version (which does not), and the message disappears from the UI.

When This Happens

The race is most likely to occur when:

  1. Model API errors/timeout — When the model provider fails (e.g. terminated, timeout), error events can trigger a history reload before the transcript is fully written. Gateway logs confirmed a terminated error on the same run where the message disappeared.
  2. WebSocket reconnect — After a disconnect/reconnect cycle, the UI reloads chat history from the server, losing any locally-added but not-yet-persisted messages.
  3. up() called from multiple event handlers — The function is called from at least 6 different places (session change, session.message event, reconnect, /new, /reset, agent change, refresh), any of which can trigger the overwrite.

Steps to Reproduce

  1. Open the webchat UI in a browser
  2. Send a message while the model provider is slow or intermittently failing
  3. Observe: the message appears briefly in the chat
  4. Observe: the message disappears when the UI reloads chat history
  5. The message may or may not appear in the session transcript on the server

Expected Behavior

  • User messages should never disappear once displayed in the UI
  • The up() function should merge server history with local messages rather than replacing the entire array
  • Or: the gateway should await the transcript persistence before broadcasting events that trigger history reloads

Suggested Fixes

Fix 1: Merge instead of replace in up()

When loading chat history from the server, preserve locally-added messages that are not yet in the server response. Match by message content + timestamp to avoid duplicates.

Fix 2: Await transcript persistence before broadcasting

In the gateway chat.send handler, await emitUserTranscriptUpdate() before broadcasting the session.message event that triggers the UI reload. This ensures the server always has the message before the UI asks for it.

Fix 3: Optimistic UI with reconciliation

Keep the local message in chatMessages with a pending flag. When up() returns server history, reconcile by replacing the pending message with the persisted version (matching by runId or content). If the server version does not include it yet, keep the local version.

Environment

  • OpenClaw gateway running on macOS (Mac Mini M4)
  • Webchat via browser (control-ui)
  • Model: nvidia/z-ai/glm-5.1 (NVIDIA API integration)
  • Gateway logs confirmed: embedded_run_agent_end with error: "terminated" on the same run where the message disappeared

Related

  • #68811 — Feature request for real-time console/activity stream in webchat (which would also help users see that their message is being processed, reducing confusion when the UI appears frozen)

extent analysis

TL;DR

The most likely fix for the disappearing user messages in the OpenClaw webchat UI is to modify the up() function to merge server history with local messages instead of replacing the entire array.

Guidance

  • Identify and implement one of the suggested fixes: merging instead of replacing in up(), awaiting transcript persistence before broadcasting, or using an optimistic UI with reconciliation.
  • Verify that the chosen fix resolves the issue by testing the webchat UI with the model provider under various conditions, such as slow or intermittent failures.
  • Consider implementing additional logging or monitoring to detect and diagnose similar issues in the future.
  • Review the related feature request (#68811) for a real-time console/activity stream in webchat, which could also help reduce user confusion when the UI appears frozen.

Example

// Merge instead of replace in `up()`
e.chatMessages = [...e.chatMessages, ...(Array.isArray(i.messages) ? i.messages : [])].filter(...)

Note: This example is a simplified illustration of the merge approach and may require adjustments to fit the actual implementation.

Notes

The choice of fix may depend on the specific requirements and constraints of the OpenClaw system, such as performance considerations or the need for real-time updates. It is essential to test and verify the chosen fix thoroughly to ensure it resolves the issue without introducing new problems.

Recommendation

Apply the "Merge instead of replace in up()" fix, as it seems to be the most straightforward and efficient solution to the problem, allowing for a seamless integration of local and server-side messages.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING