fix: allow stream to recover from 429, 400, and 408 responses - #379
Merged
Conversation
The legacy streaming data source treated every 4xx response as non-retriable, so a rate-limit (429) on the stream connection permanently stopped the stream until an app lifecycle event rebuilt the data source. Classify stream HTTP errors with LDUtil.isHttpErrorRecoverable, matching the FDv2 synchronizers and other SDKs: 400, 408, and 429 now fall through to the EventSource retry path. 401 handling is unchanged.
kinyoklion
marked this pull request as ready for review
July 30, 2026 22:02
jsonbailey
approved these changes
Jul 31, 2026
tanderson-ld
self-requested a review
July 31, 2026 14:14
tanderson-ld
approved these changes
Jul 31, 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.
The legacy streaming data source's error handler classifies every 4xx response as non-retriable (
code >= 400 && code < 500), instead of using the shared rule inLDUtil.isHttpErrorRecoverable, which exempts 400, 408, and 429. As a result, a single 429 (rate limit) on a stream connection attempt permanently stops the stream: the data source logs "non-retriable", stops the EventSource, and nothing schedules a retry. The client then silently serves cached flags with no live connection until a foreground/background transition, network change, oridentifycall happens to rebuild the data source. 400 and 408 are mishandled the same way. (SDK-2824)This only affects connect/reconnect-time HTTP errors; mid-stream drops are IO errors and already retry. The FDv2 synchronizers and the events sender already use the shared recoverability rule — the legacy FDv1 streaming path was the only streaming outlier, and every other LaunchDarkly SDK's streaming path treats 400/408/429 as retryable.
Change
StreamingDataSource.onErrornow classifies stream HTTP errors withLDUtil.isHttpErrorRecoverable(code), so 400/408/429 fall through to the existing EventSource retry path with backoff. Handling of unrecoverable codes is unchanged, including the 401 escalation toshutDown().Testing
put.streaming/retry behaviorfor client-side SDKs), verified on an emulator: onmainexactly the six recoverable-4xx cases fail (400/408/429 on initial connect and on reconnect); on this branch all of them pass, along with the fullstreamingcategory (68 tests).Note
Medium Risk
Changes live stream reconnect behavior for specific HTTP statuses; misclassification could either retry forever on bad config or still drop connections on rate limits, but logic is shared with other SDK paths already in production.
Overview
Legacy FDv1 streaming no longer treats every 4xx as fatal on connect.
StreamingDataSource.onErrornow usesLDUtil.isHttpErrorRecoverable(code)instead ofcode >= 400 && code < 500, matching FDv2 streaming, polling, and events.400, 408, and 429 are reported as retryable
LDInvalidResponseCodeFailureand stay on the EventSource backoff path instead of stopping the stream and serving cached flags until something else restarts the client. Unrecoverable 4xx (e.g. 401/403) and 401shutDown()behavior are unchanged.Unit tests cover retryable classification for 400/408/429 and a 429-then-SSE reconnect scenario.
Reviewed by Cursor Bugbot for commit 00a33ae. Bugbot is set up for automated code reviews on this repo. Configure here.