Skip to content

fix(tui): recover activity status when no runs are in flight#64862

Closed
Yanhu007 wants to merge 1 commit intoopenclaw:mainfrom
Yanhu007:fix/tui-stuck-streaming-indicator
Closed

fix(tui): recover activity status when no runs are in flight#64862
Yanhu007 wants to merge 1 commit intoopenclaw:mainfrom
Yanhu007:fix/tui-stuck-streaming-indicator

Conversation

@Yanhu007
Copy link
Copy Markdown
Contributor

Summary

The TUI status bar can get stuck on streaming indefinitely after a chat turn completes. When activeChatRunId is cleared by a concurrent event before the final event arrives, wasActiveRun is false and setActivityStatus("idle") is never called.

Changes

Add a recovery condition to finalizeRun and terminateRun: when wasActiveRun is false but no other runs are in flight (sessionRuns.size === 0 and activeChatRunId is null), still transition the activity status. Safe because it only fires when no work is active.

Fixes #64825

The TUI status bar can get stuck on "streaming" indefinitely after a
chat turn completes. This happens when `state.activeChatRunId` is
cleared by a concurrent event before the "final" event arrives, making
`wasActiveRun` false in `finalizeRun` and skipping the
`setActivityStatus("idle")` call.

Add a recovery condition: when `wasActiveRun` is false but there are
provably no other runs in flight (`sessionRuns.size === 0` and
`activeChatRunId` is null), still transition the activity status. This
is safe because it only fires when the state machine has confirmed no
other work is active.

Apply the same fix to `terminateRun` for consistency.

Fixes openclaw#64825
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 11, 2026

Greptile Summary

This PR adds a fallback condition in finalizeRun and terminateRun inside src/tui/tui-event-handlers.ts to recover from the edge case where activeChatRunId is cleared by a concurrent event before the terminal chat event arrives, leaving wasActiveRun as false and the TUI status bar permanently stuck on "streaming". The recovery guard only fires when sessionRuns is empty and activeChatRunId is null — i.e., provably no other work is in flight — making it a conservative and safe fix.

Confidence Score: 5/5

Safe to merge — the recovery condition is conservative and only fires when provably no work is in flight.

Both changed call-sites are symmetric, the guard is logically sound (noteFinalizedRun/sessionRuns.delete already removed the run before the check), and the condition cannot spuriously fire while another run is active. All remaining findings are at P2 or lower.

No files require special attention.

Reviews (1): Last reviewed commit: "fix(tui): recover activity status when n..." | Re-trigger Greptile

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8f47aacb35

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

clearActiveRunIfMatch(params.runId);
flushPendingHistoryRefreshIfIdle();
if (params.wasActiveRun) {
if (params.wasActiveRun || (sessionRuns.size === 0 && !state.activeChatRunId)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip fallback status updates for local BTW runs

handleChatEvent deliberately keeps /btw runs out of activeChatRunId tracking, but this new fallback in terminateRun updates activity when there are no in-flight runs even when wasActiveRun is false. If a local BTW run ends with aborted/error, the TUI status can now flip to aborted/error and stay there despite no main chat run being active, which is a regression from prior behavior where non-active BTW runs did not mutate global activity state.

Useful? React with 👍 / 👎.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 26, 2026

Closing this as implemented after Codex automated review.

Current main already fixes the user-visible stuck TUI streaming status via the shipped #67401 client-side streaming watchdog. The watchdog resets the active run to idle after delta silence, is covered by focused TUI handler tests, and shipped in v2026.4.15; this PR's fallback is no longer needed and would broaden non-active abort/error status updates.

Best possible solution:

Close #64862 and keep the shipped #67401 watchdog implementation. If a stuck TUI status is still reproducible on current releases, it should be tracked as a fresh bug with event-ordering logs, version details, affected plugin/channel context, and a regression test for the new ordering.

What I checked:

So I’m closing this as already implemented rather than keeping a duplicate issue open.

Codex Review notes: model gpt-5.5, reasoning high; reviewed against d54d2d6b9b8a; fix evidence: release v2026.4.15, commit 352527393079.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TUI stuck on 'streaming' indicator after run completes — finalizeRun() doesn't transition UI when wasActiveRun is false

1 participant