fix(site/src/pages/AgentsPage): clear streamed message when a chat errors - #28277
Conversation
…rors When a chat stopped with an error, the WebSocket error handler set the chat status and stored the error but never cleared the in-flight stream state. A tool call that had no result yet kept its running status, so the partial output (for example "Creating workspace...") kept showing a spinner even though the chat had errored. Refreshing showed the correct status because the stream state is transient. Treat the error event as a terminal turn boundary. Discard buffered parts, gate late parts from the finished turn, and clear the stream state. The error callout still surfaces the failure.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 14850273e2
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…r clearing stream Add a ChatPageContent story that streams a create_workspace tool call and applies the terminal error, asserting the running row is removed and the failure callout stays. Also drop test comments that restated the assertions below them.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0edc5931c0
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… story Mount the running tool state, then trigger the terminal error during play and assert the running row is removed while the failure callout remains. Previously the story set the cleared state before mount, so it passed even if the error path stopped clearing the stream.
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep it up! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a5133968e6
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A prior commit set streamReportedWaiting on the error path so late parts from the errored turn would be dropped. The gate is only reopened by a status event, and the server delivers parts and status on separate unordered paths, so a new turn's first part can arrive before its running status and be silently dropped, corrupting the response. Drop the gate and keep clearing the stream state and buffered parts. This preserves the fix for the leftover spinning tool while never dropping a part that belongs to the next turn. A part already queued from the errored turn can still briefly reappear afterward, which is a cosmetic race that the next status event or a refresh resolves.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a2528d230b
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…at error A part already queued from an errored turn can arrive after the error and repopulate the cleared stream. Render the failed state without that leftover so it does not show as a live row, and clear the stored error when a new turn's status arrives so the next stream renders. Also reset the story store between runs so the error transition is exercised each time.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d56144c66e
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…inal errors The render suppression forced hasAccumulatedOutput off whenever streamError was set, but request and parse errors set streamError while the stream is intentionally still running. Gate the suppression on the chat status being error, which only the terminal stream error sets, so a genuinely live assistant row keeps rendering for non-terminal errors.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ce7ad11499
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…to terminal status Clear the stored stream error only when an errored chat begins a new turn, so a status from a still-running turn does not wipe an unrelated request-failure callout. Suppress stream output whenever the chat status is error, not only while the error is set, so a stale leftover does not reappear as a streaming row while a retry is in flight.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3d8182fe40
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…connect phases The retry and reconnect branches still passed the unsuppressed accumulated-output flag, so a stale leftover from a failed turn could render under a reconnect banner while the chat status is error. Pass the terminally suppressed value through those branches too, and tighten the comment to state only the non-obvious invariant.
|
@codex review |
|
Codex Review: Didn't find any major issues. Delightful! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Fixes CODAGT-622
When a chat stopped with an error, the streamed message and its running status stayed on screen instead of clearing. A chat that hit "Response failed: The chat session ended unexpectedly" while "Creating workspace..." kept showing a spinner. Refreshing showed the correct status because the stream state is transient, confirming it was frontend-only.
The WebSocket
errorhandler set the chat status and stored the error but never cleared the in-flightstreamState. A tool call with no result yet keeps a derivedrunningstatus, so the leftover row kept spinning. Treat theerrorevent as a terminal turn boundary: discard buffered parts, gate late parts from the finished turn, and clear the stream state. The error callout still surfaces the failure.On this error path the partial output is not committed durably server-side (partial persistence only runs on the interrupt path), so clearing the preview converges the live UI with what a refresh already shows.
Implementation plan and fix evaluation
Root cause was confirmed with a store-level test that fails the moment
clearStreamState()runs on the error path. Four independent fix designs were evaluated (clearstreamState; finalize orphaned tools toerror; render-layer-only; combinations). All rejected the render-layer-only fix becauseisStreaming/isChatCompletedinChatPageContentderive fromstreamState !== nulland would stay wrong. Chosen approach clearsstreamStateand adds hardening against a pending flush timer or same-framemessage_partre-populating the stream after the clear.Backend verification:
flushActiveStatepartial persistence incoderd/x/chatd/chatloop/chatloop.goruns only onErrInterrupted, not on the "stream closed unexpectedly" provider-error path (chaterror/classify.go), so the partial output is not recoverable server-side here and discarding the transient preview matches the refreshed view.Generated by Coder Agents.