openclaw - 💡(How to fix) Fix Control UI: messages flash-disappear on turn completion (race in session.message handler) [2 comments, 2 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#71371Fetched 2026-04-26 05:13:29
View on GitHub
Comments
2
Participants
2
Timeline
5
Reactions
0
Author
Participants
Timeline (top)
commented ×2cross-referenced ×2closed ×1

After an assistant turn completes, messages visually disappear from the Control UI chat view. Browser refresh brings them back immediately. The bug is a race condition in the session.message WebSocket event handler.

Root Cause

Root Cause (from source analysis)

Fix Action

Workaround

Browser refresh after each response. No permanent workaround available.

Code Example

function gE(e, t) {
  if (e.chatRunId) {
    n.pendingSessionMessageReloadSessionKey = r;  // defer
    return;
  }
  n.pendingSessionMessageReloadSessionKey = null;
  Zs(e);  // reload immediately
}

---

// In Zs: don't null chatStream before the response arrives
// Move e.chatStream = null AFTER e.chatMessages = ...
RAW_BUFFERClick to expand / collapse

Summary

After an assistant turn completes, messages visually disappear from the Control UI chat view. Browser refresh brings them back immediately. The bug is a race condition in the session.message WebSocket event handler.

Environment

  • OpenClaw v2026.4.23, gateway mode: local
  • WSL2 (Linux 6.6.87.2-microsoft-standard-WSL2), Firefox 151
  • Claude Opus 4.7 via claude-cli backend
  • Session with ~27 child subagent sessions, ~219k tokens

Root Cause (from source analysis)

In control-ui/assets/index-*.js, the session.message handler (gE) checks chatRunId:

function gE(e, t) {
  if (e.chatRunId) {
    n.pendingSessionMessageReloadSessionKey = r;  // defer
    return;
  }
  n.pendingSessionMessageReloadSessionKey = null;
  Zs(e);  // reload immediately
}

When chatRunId is cleared (turn finished) before session.message arrives, Zs() fires immediately. Zs does:

  1. Sets chatLoading = true
  2. Fires async chat.history request
  3. Clears chatStream = null and chatStreamStartedAt = null — this removes the streamed content from the DOM
  4. When chat.history resolves, sets chatMessages from response — content reappears

The visual gap between step 3 (streamed content cleared) and step 4 (history loaded) causes messages to disappear. On refresh, chat.history is the only source so there's no gap.

Why it's worse on some setups

  • WSL2 networking: loopback through virtual adapter adds latency to chat.history round-trip
  • Large sessions: more child sessions and history = slower chat.history response
  • On native localhost with small sessions, the gap is likely imperceptible

Suggested Fix

Don't clear chatStream until chat.history has returned successfully. Keep the existing streamed content visible as a fallback during the reload:

// In Zs: don't null chatStream before the response arrives
// Move e.chatStream = null AFTER e.chatMessages = ...

Or: preserve chatMessages during reload and only replace them once new data arrives (optimistic UI pattern).

Reproduction

  1. Use Control UI via WSL2 or any setup with >1ms localhost latency
  2. Have a session with accumulated subagent child sessions
  3. Send any message and wait for the response to complete
  4. Message will visually disappear when the turn finalizes
  5. Browser refresh brings it back

Workaround

Browser refresh after each response. No permanent workaround available.

extent analysis

TL;DR

Modify the Zs function to preserve the existing chatStream content until the chat.history response is received.

Guidance

  • Identify the Zs function in control-ui/assets/index-*.js and modify it to delay clearing chatStream until after the chat.history response is processed.
  • Consider implementing an optimistic UI pattern to preserve chatMessages during reload and replace them only when new data arrives.
  • Verify the fix by reproducing the issue and checking if the messages remain visible after the turn completes.
  • Test the fix with different session sizes and network latency to ensure it works in various scenarios.

Example

// In Zs: don't null chatStream before the response arrives
// Move e.chatStream = null AFTER e.chatMessages = ...
// Example:
function Zs(e) {
  // ...
  chatLoading = true;
  chat.history.request().then(response => {
    // Process response and update chatMessages
    e.chatMessages = response.data;
    // Clear chatStream only after new data is received
    e.chatStream = null;
    chatStreamStartedAt = null;
  });
}

Notes

The suggested fix assumes that the Zs function is the root cause of the issue. However, the actual implementation may vary depending on the surrounding code and requirements.

Recommendation

Apply the workaround by modifying the Zs function to preserve the existing chatStream content, as this is a more targeted and less invasive change compared to other potential solutions.

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