Skip to content

fix(sse): honor disconnect/dispose and cap error-path reconnects#2277

Merged
sharjeelyunus merged 1 commit into
mainfrom
cursor/critical-bug-remediation-d9f6
Jun 13, 2026
Merged

fix(sse): honor disconnect/dispose and cap error-path reconnects#2277
sharjeelyunus merged 1 commit into
mainfrom
cursor/critical-bug-remediation-d9f6

Conversation

@cursor

@cursor cursor Bot commented Jun 12, 2026

Copy link
Copy Markdown

Bug and impact

Trigger: A screen subscribes to an SSE API (invokeAPI with listenForChanges / SSE provider). The user calls disconnectSSE, navigates away (screen dispose), or the endpoint errors repeatedly.

Impact:

  1. Disconnect/dispose ignored: disconnect() and dispose() set _manuallyDisconnected but reconnect logic never checked it. dispose() also cleared the set immediately, so delayed onDone callbacks still scheduled reconnects against closed HTTP clients — background network activity, battery drain, and possible duplicate listeners.
  2. Unbounded error reconnects: _handleSSEError received reconnectAttempts by value; increments inside the handler did not update the outer counter in _connectSSE. With autoReconnect: true (default), every stream error retried with attempt == 0, bypassing maxReconnectAttempts (default 5) and causing an infinite reconnect loop on persistent failures.

Root cause

  • _manuallyDisconnected was write-only (set in disconnect/dispose, never read in onDone/_handleSSEError).
  • dispose() cleared _manuallyDisconnected after cancelling subscriptions.
  • Error-path reconnect used a by-value int instead of shared mutable attempt state.

Fix

  • Add _disposed flag and central _shouldReconnect() guard (checks dispose, manual disconnect, autoReconnect, and maxReconnectAttempts).
  • Re-check guard inside delayed reconnect callbacks before calling connect().
  • Share reconnect attempt counter via List<int> so error and onDone paths increment the same counter.
  • Stop clearing _manuallyDisconnected in dispose().

Validation

  • Extended modules/ensemble/test/sse_provider_dispose_test.dart with reconnect guard tests (disconnect, dispose, max attempts, shared counter).
  • Run from modules/ensemble: flutter test test/sse_provider_dispose_test.dart

Duplicate check

Open in Web View Automation 

SSE auto-reconnect ignored disconnectSSE and screen dispose because
_manuallyDisconnected was never checked before scheduling reconnects,
and dispose() cleared that set before delayed callbacks could run.

The error handler also passed reconnectAttempts by value, so the outer
counter never advanced and maxReconnectAttempts was bypassed on errors.

Guard reconnect with _shouldReconnect (dispose + manual disconnect) and
share attempt state via a list so error-driven retries respect the cap.

Co-authored-by: Sharjeel Yunus <sharjeelyunus@users.noreply.github.com>
@sharjeelyunus sharjeelyunus marked this pull request as ready for review June 13, 2026 17:57
@sharjeelyunus sharjeelyunus merged commit 01e667e into main Jun 13, 2026
5 checks passed
@sharjeelyunus sharjeelyunus deleted the cursor/critical-bug-remediation-d9f6 branch June 13, 2026 17:58
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