Skip to content

Clear the stored stream error when a new turn starts streaming - #238

Merged
acoliver merged 1 commit into
mainfrom
issue218
Sep 7, 2026
Merged

acoliver merged 1 commit into
mainfrom
issue218

Conversation

@acoliver

@acoliver acoliver commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Fixes #218

After any stream error in a conversation, every later turn rendered nothing while streaming: no thinking, no token-by-token text, with the answer appearing all at once at the end.

Cause

last_error was written by reduce_stream_error and cleared nowhere. A grep for last_error = None, last_error: None and last_error.take() across src/ui_gpui/ returned nothing, which is the whole bug in one line.

streaming_state_from_snapshot checks last_error before it checks whether a stream is active, so once a turn died the conversation stayed on StreamingState::Error. The render gate only draws the streaming bubble for StreamingState::Streaming, and thinking renders inside that bubble, so both were suppressed. The store kept doing its job the entire time, filling thinking_buffer and copying it into thinking_content; nothing ever drew it.

It looked intermittent rather than permanent because a later successful finalize removes the whole streaming-state entry, taking last_error with it.

Fix

Clear last_error at the stream-start seam rather than special-casing the projection that reads it. show_thinking_for_target is that seam (the presenter sends ShowThinking when a stream starts and before sending a message), so it now clears the field: an error recorded by a finished turn must not describe the turn now running.

changed also accounts for a cleared error, so the projection refreshes and the target rejoins the active set. Without that, a turn starting with thinking already visible and the same model would clear the error without marking the state changed, and the view would not re-render.

On the check order, which the issue also raised

Left as error-first, deliberately.

After this reset, every legitimately active stream has already cleared last_error, so the order cannot misfire on a real turn sequence. The only remaining case where both could be set is a stale delta arriving after its own turn's error with no new stream start, and there Error is the accurate projection. Swapping the checks would resurrect dead partial content and hide the failure, so the reset is the fix and the reorder is not needed.

Tests

a_new_turn_after_a_stream_error_projects_streaming_again drives error, then stream start, then a delta through the store, and asserts the projection returns to Streaming with the new turn's thinking buffer intact.

It was written first and failed against unmodified code with the reported symptom, a provider 429:

  left: Error("429 too many requests")
 right: Streaming { content: "turn-two delta", done: false }

The test lives in a new streaming_error_tests.rs sibling module. Adding it to mod_tests.rs pushed that file to 1067 lines, over the repo's 1000-line gate, so it moved rather than anything being deleted to fit; mod_tests.rs is unchanged in this diff.

Verification

cargo fmt --all -- --check; clippy with the full CI lint set; cargo test --lib --tests (1976 passed, 0 failed, which is main's 1975 plus this regression test); cargo xtask guard; lizard -C 50 -L 100 -w src/; the 1000-line file gate. No lint suppressions, no new dependencies, no existing test weakened.

Independent of the steering work in #236 and #237: this touches only app_store_streaming.rs and the chat view test modules.

@coderabbitai

coderabbitai Bot commented Sep 5, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 7 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 7c5f2e00-460b-483f-9d30-d5006d9bbd56

📥 Commits

Reviewing files that changed from the base of the PR and between 0ad5b42 and ab41478.

📒 Files selected for processing (3)
  • src/ui_gpui/app_store_streaming.rs
  • src/ui_gpui/views/chat_view/mod.rs
  • src/ui_gpui/views/chat_view/streaming_error_tests.rs

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@acoliver

acoliver commented Sep 5, 2026

Copy link
Copy Markdown
Owner Author

The Provider-backed E2E check is red for a reason unrelated to this change: the Ollama account no longer subscribes, and the workflow's fallback model minimax-m2.7:cloud is behind a subscription, so the job gets HTTP 402 and fails in under half a second without reaching any application code.

Tracked in #240, which has the detail and the options. The same failure is on #236 and will appear on #239.

Every other check on this PR passes: Coverage, both platform builds, the clippy and structural lint gate, formatting, tests, and CodeRabbit.

A stream error wrote last_error into the conversation's streaming state
and nothing ever cleared it. The chat view projection checks last_error
before it checks whether a stream is active, so once a turn died the
conversation stayed on StreamingState::Error: the streaming bubble and
the thinking rendered inside it were suppressed for every later turn,
and the answer appeared only when some later successful finalize
removed the whole state entry.

Fix the state transition at its source. show_thinking_for_target is the
stream-start seam (the presenter sends ShowThinking both when a stream
starts and before sending a message), so it now clears last_error for
the target. An error recorded by a finished turn must not describe the
turn now running. changed also accounts for a cleared error so the
projection refreshes and the target rejoins the active set.

The check order in streaming_state_from_snapshot stays error-first.
After the reset, every legitimately active stream has already cleared
last_error, so the order cannot misfire on real turn sequences. The
only remaining overlap is a stale delta arriving after its own turn's
error with no new stream start, and for that case Error is the accurate
projection; swapping the checks would instead resurrect dead partial
content and hide the failure.

The new regression test drives error, stream start, and a delta through
the store and asserts the projection returns to Streaming with the new
turn's thinking buffer intact.
@acoliver
acoliver merged commit 0501fc2 into main Sep 7, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A stream error permanently disables live streaming display for that conversation: last_error is never cleared

1 participant