fix: schedule reconnect after parse error during streaming#134
Open
kinyoklion wants to merge 3 commits intomainfrom
Open
fix: schedule reconnect after parse error during streaming#134kinyoklion wants to merge 3 commits intomainfrom
kinyoklion wants to merge 3 commits intomainfrom
Conversation
In `ReconnectingRequest::poll_next`, parser errors propagated through `process_bytes(...)?` while in the `Connected` state previously returned the error without scheduling a reconnect. The next poll continued draining the broken response body before finally hitting end-of-body and emitting a spurious `UnexpectedEof` before the reconnect kicked in. Schedule a reconnect on parse errors when reconnect is enabled. The error is still returned to the caller; the only behavioral change is that the next poll cleanly transitions to reconnect rather than draining the broken body. Adds `parser_error_schedules_reconnect_immediately` test verifying that the next stream item after a parse error is the reconnect's `Connected`, not an extra error. Contributes to launchdarkly/rust-server-sdk#116.
c7ae337 to
7917038
Compare
3 tasks
- Match `Error::InvalidLine(_)` specifically so a regression that produces a different error class is caught. - Replace `tokio::time::timeout(...).ok()` with `.expect(...)` so a flaky timeout produces a clear failure mode rather than a silent "got None" further down. - Bump the timeout to 2 s to absorb slower CI runners. - Drop the decorative `expect_at_least(2)` -- mockito's default `assert_on_drop` is false, so it enforced nothing; the `items[2] == Connected` assertion already requires the reconnect.
6 tasks
The previous docstring told consumers "Do not use the stream after it returned an error!" That instruction contradicts how the stream actually works: most errors are non-terminal, and the reconnect machinery only runs if the consumer keeps polling. Stream termination is signalled by `Poll::Ready(None)`, not by an error. Reword the docstring to describe the real contract: errors are informational, keep polling, the stream ends when poll_next returns None.
keelerm84
approved these changes
May 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every error path in
ReconnectingRequest::poll_nextduring theConnectedstate sets state toWaitingToReconnectbefore returning the error — except for parser errors propagated throughprocess_bytes(...)?. After a parse error, the state stayed atConnectedand the next poll continued draining the (already-broken) response body before finally hitting end-of-body and emitting a spuriousUnexpectedEofbefore the reconnect actually kicked in.This PR schedules a reconnect on parse errors when reconnect is enabled, matching the pattern used by every other error path. The error is still returned to the caller; the only behavioral change is that the next poll cleanly transitions to reconnect rather than draining the broken body.
Before:
Connected,Err(InvalidLine),Err(UnexpectedEof),Connected, …After:
Connected,Err(InvalidLine),Connected, …Context
Surfaced while investigating launchdarkly/rust-server-sdk#116 (
StreamingDataSourcesilently shuts down on stream errors). That report has two contributing bugs; this PR addresses the rust-eventsource-client side. The rust-server-sdk side will be fixed in a follow-up PR — together they restore the reconnection contract: the eventsource-client owns reconnection, the SDK keeps polling and trusts it.Tracked in SDK-2345.
Test plan
parser_error_schedules_reconnect_immediately(eventsource-client/src/client.rs) — verifies the next stream item after a parse error isConnectedfrom the reconnect, not a spuriousUnexpectedEof.cargo test— 60 lib tests + 2 doc tests pass; no regressions.Note
Medium Risk
Changes streaming error-handling semantics by scheduling reconnects on parser failures, which can affect how downstream consumers observe errors and reconnect timing. Scope is small and covered by a new integration-style test, but touches core stream state transitions.
Overview
Fixes reconnection behavior after SSE parse errors during streaming. When
EventParser::process_bytesfails in theConnectedstate, the client now (whenReconnectOptions::reconnectis enabled) transitions toWaitingToReconnectbefore yielding the parse error, avoiding continued draining of a broken response body and the resulting follow-on errors.Updates
Client::streamdocs to clarify that stream errors are non-terminal and that the stream only ends onPoll::Ready(None), and adds a new test (parser_error_schedules_reconnect_immediately) asserting the sequenceConnected -> Err(InvalidLine) -> Connected.Reviewed by Cursor Bugbot for commit ddea87d. Bugbot is set up for automated code reviews on this repo. Configure here.