You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
ExponentialBackoff::retry computes the reconnect multiplier as 2^current_times using u32::pow. With max_times unset, current_times can reach 32; in debug builds this overflows and panics, while release arithmetic can wrap and produce an incorrect delay. The resulting behavior makes a long-lived SSE client vulnerable to a retry-loop panic or unexpectedly short backoff.
Proposed direction
Saturate the multiplier at u32::MAX once current_times reaches the bit width, then use saturating multiplication for the base duration. This keeps the existing unbounded policy while making the delay monotonic and panic-free.
Verification
A focused regression at current_times = 32 reproduces the overflow on the pre-fix implementation and passes with the saturating calculation. The rmcp test suite and clippy checks pass on the proposed patch.
Would maintainers like this fix submitted as a PR?
The saturating approach you describe is the right call. Concretely, I'd clamp the shift once current_times reaches the bit width so the multiplier saturates at u32::MAX rather than wrapping, then use saturating_mul on the base duration so the delay stays monotonic and the policy stays unbounded without ever panicking.
For the regression I'd cover three points rather than just the overflow: the boundary at current_times = 32, one value past it to confirm the delay stops growing instead of resetting, and a low value to confirm existing behavior is unchanged. The middle one is the case that actually distinguishes a saturating fix from a wrapping one.
Will open a PR shortly unless someone is already on it.
Activity
acamarata commentedon Aug 25, 2026
I'd like to take this one.
The saturating approach you describe is the right call. Concretely, I'd clamp the shift once
current_timesreaches the bit width so the multiplier saturates atu32::MAXrather than wrapping, then usesaturating_mulon the base duration so the delay stays monotonic and the policy stays unbounded without ever panicking.For the regression I'd cover three points rather than just the overflow: the boundary at
current_times = 32, one value past it to confirm the delay stops growing instead of resetting, and a low value to confirm existing behavior is unchanged. The middle one is the case that actually distinguishes a saturating fix from a wrapping one.Will open a PR shortly unless someone is already on it.
fix(sse): default max_delay to None to preserve unbounded backoff
fix(sse): saturate exponential reconnect backoff to avoid overflow pa…