Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
Two related bugs in the TUI's streaming-status handling cause the activity indicator to desync from the actual run state:
- False "idle" while the run is still active. When the agent is busy with tool calls and emits no chat
delta events for 30s, the streaming watchdog fires and sets status to idle. The run is not actually idle — the gateway is still executing tools and will emit more deltas and a final event later.
- Stuck "streaming" forever after a missed final event. If the
final chat event is lost (e.g. WebSocket drop without replay on reconnect), the TUI can sit on "streaming" indefinitely (observed: >1 hour). The 30s watchdog should catch this, but does not always fire in practice, and even when the final is lost the assistant output stays in the session store but is never rendered.
Root cause sketch
In dist/tui-CD9-89mO.js (~line 2720–2740, handleChatEvent):
if (evt.state === "delta") {
const displayText = streamAssembler.ingestDelta(evt.runId, evt.message, state.showThinking);
if (!displayText) return;
chatLog.updateAssistant(displayText, evt.runId);
setActivityStatus("streaming");
if (state.activeChatRunId === evt.runId) armStreamingWatchdog(evt.runId);
}
The watchdog is only (re)armed on chat delta events. handleAgentEvent (~line 2802, stream === "tool") handles tool phases but never rearms the streaming watchdog. So a run that is actively executing tools looks dead to the watchdog once the last delta is 30s old — producing Bug 1.
For Bug 2, client.onConnected (~line 3950) triggers loadHistory() on reconnect but does not clear state.activeChatRunId or clearStreamingWatchdog(). If the WS dropped mid-run and the gateway finished while no subscriber was listening, the final event is lost (no backlog/replay in broadcastChatFinal). The TUI comes back up still believing activeChatRunId is set and watchdog state is stale.
Suggested fix (words, not code)
- Bug 1: In
handleAgentEvent, when a stream === "tool" event arrives for state.activeChatRunId, rearm the streaming watchdog (same armStreamingWatchdog(runId) call as the delta path). Tool activity is proof-of-life; the watchdog should track "gateway is silent about this run", not specifically "no chat deltas".
- Bug 2: In
client.onConnected, when reconnected === true, clear activeChatRunId, call clearStreamingWatchdog(), and set status to idle before loadHistory(). Any truly in-flight run will re-emit deltas and the TUI will re-adopt it; a run that finished during disconnect surfaces correctly via history reload instead of hanging.
- Optional, larger: event replay on resubscribe so missed
final events are delivered and finalizeAssistant still runs — touches the gateway broadcast path, not strictly necessary if history-reload on reconnect is acceptable.
Steps to reproduce
Bug 1 — watchdog fires during long tool calls
- Start
openclaw tui.
- Send a prompt that causes the agent to do ≥30s of tool work without producing any assistant text between tools (e.g. several sequential
read / exec grep calls).
- Wait 30 seconds after the last chat delta.
Bug 2 — stuck streaming, missed final
- Start
openclaw tui and send a long-running prompt.
- Trigger a transient WebSocket disconnect during the run (on WSL2 / Tailscale this happens occasionally on its own; otherwise restart the gateway mid-run or briefly drop the network).
- Let the TUI reconnect automatically.
Expected behavior
- Bug 1: While the gateway is actively processing a run — including tool-call phases without chat deltas — the TUI keeps the status indicator at
streaming (or equivalent "working"). The watchdog only fires when the gateway is genuinely silent about the run for ≥30s across all event streams (chat + agent), not when it is just between deltas during tool work.
- Bug 2: After a WebSocket reconnect the TUI does not remain in a stale
streaming state. Either (a) the run is still in flight and new deltas re-establish it, or (b) the run finished during the disconnect and the reload of chat.history shows the final assistant output, with status back to idle. In no case should the user have to send a dummy message to recover the last output.
Actual behavior
- Bug 1: After 30s of tool activity without chat deltas, the TUI logs
streaming watchdog: no stream updates for 30s; resetting status. The backend may have dropped this run silently — send a new message to resync. and the status indicator switches to idle, even though the run is still executing server-side. When the run eventually finishes, the final output still arrives and is appended, but during the interval the user has no indication that the agent is still working.
- Bug 2: The status indicator stays on
streaming long after the gateway has finished (observed up to >1 hour). No streaming watchdog message appears. The final assistant text is missing from the chat buffer. Sending any new message triggers a chat.history reload which surfaces the missing output — confirming it was generated on the gateway but never delivered/rendered.
OpenClaw version
2026.4.15
Operating system
Ubuntu 24.04.3 LTS
Install method
npm install -g openclaw
Model
anthropic/claude-opus-4-7 (main agent), with anthropic/claude-sonnet-4-20250514 as default
Provider / routing chain
Direct Anthropic API for the models above (auth: anthropic:default, subscription/Max plan)
Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
- False-idle misleads the user into thinking the agent hung or stopped; they may re-send, creating duplicate runs.
- False-streaming hides that output is already available; user waits indefinitely or manually resets the session.
- Neither bug loses data on the gateway side — output is always in the session store — but the TUI doesn't surface it.
Additional information
No response
Bug type
Behavior bug (incorrect output/state without crash)
Beta release blocker
No
Summary
Two related bugs in the TUI's streaming-status handling cause the activity indicator to desync from the actual run state:
deltaevents for 30s, the streaming watchdog fires and sets status toidle. The run is not actually idle — the gateway is still executing tools and will emit more deltas and afinalevent later.finalchat event is lost (e.g. WebSocket drop without replay on reconnect), the TUI can sit on "streaming" indefinitely (observed: >1 hour). The 30s watchdog should catch this, but does not always fire in practice, and even when the final is lost the assistant output stays in the session store but is never rendered.Root cause sketch
In
dist/tui-CD9-89mO.js(~line 2720–2740,handleChatEvent):The watchdog is only (re)armed on chat
deltaevents.handleAgentEvent(~line 2802,stream === "tool") handles tool phases but never rearms the streaming watchdog. So a run that is actively executing tools looks dead to the watchdog once the last delta is 30s old — producing Bug 1.For Bug 2,
client.onConnected(~line 3950) triggersloadHistory()on reconnect but does not clearstate.activeChatRunIdorclearStreamingWatchdog(). If the WS dropped mid-run and the gateway finished while no subscriber was listening, the final event is lost (no backlog/replay inbroadcastChatFinal). The TUI comes back up still believingactiveChatRunIdis set and watchdog state is stale.Suggested fix (words, not code)
handleAgentEvent, when astream === "tool"event arrives forstate.activeChatRunId, rearm the streaming watchdog (samearmStreamingWatchdog(runId)call as the delta path). Tool activity is proof-of-life; the watchdog should track "gateway is silent about this run", not specifically "no chat deltas".client.onConnected, whenreconnected === true, clearactiveChatRunId, callclearStreamingWatchdog(), and set status toidlebeforeloadHistory(). Any truly in-flight run will re-emit deltas and the TUI will re-adopt it; a run that finished during disconnect surfaces correctly via history reload instead of hanging.finalevents are delivered andfinalizeAssistantstill runs — touches the gateway broadcast path, not strictly necessary if history-reload on reconnect is acceptable.Steps to reproduce
Bug 1 — watchdog fires during long tool calls
openclaw tui.read/exec grepcalls).Bug 2 — stuck streaming, missed final
openclaw tuiand send a long-running prompt.Expected behavior
streaming(or equivalent "working"). The watchdog only fires when the gateway is genuinely silent about the run for ≥30s across all event streams (chat + agent), not when it is just between deltas during tool work.streamingstate. Either (a) the run is still in flight and new deltas re-establish it, or (b) the run finished during the disconnect and the reload ofchat.historyshows the final assistant output, with status back toidle. In no case should the user have to send a dummy message to recover the last output.Actual behavior
streaming watchdog: no stream updates for 30s; resetting status. The backend may have dropped this run silently — send a new message to resync.and the status indicator switches toidle, even though the run is still executing server-side. When the run eventually finishes, the final output still arrives and is appended, but during the interval the user has no indication that the agent is still working.streaminglong after the gateway has finished (observed up to >1 hour). Nostreaming watchdogmessage appears. The final assistant text is missing from the chat buffer. Sending any new message triggers achat.historyreload which surfaces the missing output — confirming it was generated on the gateway but never delivered/rendered.OpenClaw version
2026.4.15
Operating system
Ubuntu 24.04.3 LTS
Install method
npm install -g openclaw
Model
anthropic/claude-opus-4-7(main agent), withanthropic/claude-sonnet-4-20250514as defaultProvider / routing chain
Direct Anthropic API for the models above (auth:
anthropic:default, subscription/Max plan)Additional provider/model setup details
No response
Logs, screenshots, and evidence
Impact and severity
Additional information
No response