Skip to content

fix: schedule reconnect after parse error during streaming#134

Open
kinyoklion wants to merge 3 commits intomainfrom
rl/sdk-2345/parser-error-reconnect-state
Open

fix: schedule reconnect after parse error during streaming#134
kinyoklion wants to merge 3 commits intomainfrom
rl/sdk-2345/parser-error-reconnect-state

Conversation

@kinyoklion
Copy link
Copy Markdown
Member

@kinyoklion kinyoklion commented May 8, 2026

Summary

Every error path in ReconnectingRequest::poll_next during the Connected state sets state to WaitingToReconnect before returning the error — except for parser errors propagated through process_bytes(...)?. After a parse error, the state stayed at Connected and the next poll continued draining the (already-broken) response body before finally hitting end-of-body and emitting a spurious UnexpectedEof before 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 (StreamingDataSource silently 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

  • New test parser_error_schedules_reconnect_immediately (eventsource-client/src/client.rs) — verifies the next stream item after a parse error is Connected from the reconnect, not a spurious UnexpectedEof.
  • 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_bytes fails in the Connected state, the client now (when ReconnectOptions::reconnect is enabled) transitions to WaitingToReconnect before yielding the parse error, avoiding continued draining of a broken response body and the resulting follow-on errors.

Updates Client::stream docs to clarify that stream errors are non-terminal and that the stream only ends on Poll::Ready(None), and adds a new test (parser_error_schedules_reconnect_immediately) asserting the sequence Connected -> Err(InvalidLine) -> Connected.

Reviewed by Cursor Bugbot for commit ddea87d. Bugbot is set up for automated code reviews on this repo. Configure here.

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.
@kinyoklion kinyoklion force-pushed the rl/sdk-2345/parser-error-reconnect-state branch from c7ae337 to 7917038 Compare May 8, 2026 21:32
@kinyoklion kinyoklion marked this pull request as ready for review May 8, 2026 21:39
@kinyoklion kinyoklion requested a review from a team as a code owner May 8, 2026 21:39
@kinyoklion kinyoklion changed the title fix: schedule reconnect after parse error during streaming [SDK-2345] fix: schedule reconnect after parse error during streaming May 8, 2026
- 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.
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.
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.

2 participants