bug(desktop): failed turn can remain stuck on Waiting for model output
- Dominant language
- TypeScript
- Stars
- 5.4k
- Forks
- 502
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 715
Description
## What happened
Desktop can render a terminal-looking failed-turn banner and `等待模型输出…` / `Waiting for model output…` for the same tail turn at the same time. In the observed session this contradictory state persisted for several minutes: the banner said the turn had failed with no diagnostic details, while the running indicator continued counting upward and the composer remained in the active-turn state.
The fallback title (`出错了,暂时无法确定原因。`) is reasonable when a settled failed turn has no classified reason. The defect is that the same turn is simultaneously presented as failed and still running, leaving the user unable to tell whether to wait, stop, continue, or resend.
The renderer has two independent inputs that make this state deterministic at the component boundary:
- `TurnView` renders `.maka-turn-failed-banner` when `turn.status === 'failed'` and a failed reason is available.
- The same `TurnView` renders `TurnRunningStatus` whenever `liveStreaming.runningStatus` is true.
There is no terminal-status fence between them. `ChatView` supplies `liveStreaming` to the tail turn from the separate live/session-running projection, so a durable failed turn plus a stale or still-active liveness witness produces both surfaces.
Expected: one coherent state for a turn. A terminal failed turn must not show an ongoing model-wait indicator. If the run is genuinely still active after a non-terminal error, the UI must not present that error as the terminal outcome yet.
Actual: the UI can state both “failed” and “waiting for model output” indefinitely.
## How to reproduce
Component-level deterministic reproduction:
1. Render `TurnView` with a recorded turn whose `status` is `failed`, an unknown/available failed-reason label, and at least one errored tool.
2. Pass `liveStreaming={{ runningStatus: true }}` for that same turn.
3. Observe that the failed banner and `Waiting for model output…` both render.
Observed Desktop path:
1. Run a tool-using turn in Desktop.
2. Let the turn enter the unknown/tool-failure presentation while the live-turn/session-running witness remains active.
3. Observe the red failed banner followed by `等待模型输出…` with an elapsed timer.
4. The captured occurrence remained in this state for more than four minutes, so it was not only a one-frame settlement transition.
The exact event-order trigger still needs isolation. The report intentionally does not attribute it to the network: the evidence establishes a renderer state contradiction, not why the runtime/live projection diverged.
## Environment
- Maka source commit inspected: `30cf0beb0def57626df3fdbf8c7ab4d0d3436896`
- Surface: Desktop conversation
- Locale: zh-CN
## Logs, screenshots, or additional context
Relevant code:
- `packages/ui/src/chat-turn.tsx`: the failed banner is gated by `turn.status === 'failed'`; the running status immediately below is independently gated by `props.liveStreaming.runningStatus`.
- `packages/ui/src/chat-view.tsx`: the tail turn receives `liveStreaming` from `runningStatus` / live-turn state independently of its projected recorded status.
- `apps/desktop/src/renderer/use-shell-live-turn.ts`: `showRunningStatus` follows `turnActive`, which is derived from the renderer arm and `runningTurnIds`, not from the transcript turn's terminal status.
Acceptance should cover the actual composition, not only each surface separately: a recorded failed tail turn and a running witness for the same identity must reconcile to one user-facing state, while a genuinely concurrent sibling turn must remain visibly running.
Contributor guide
Assessment
This issue has not been assessed yet.