Skip to content

fix: guard completer against race condition in web connect#87

Open
jin123456bat wants to merge 1 commit into
felangel:mainfrom
jin123456bat:fix/guard-completer-race-condition
Open

fix: guard completer against race condition in web connect#87
jin123456bat wants to merge 1 commit into
felangel:mainfrom
jin123456bat:fix/guard-completer-race-condition

Conversation

@jin123456bat

Copy link
Copy Markdown

Problem

When a WebSocket opens successfully but later encounters an error (e.g., network drops), both onOpen and onError fire on the same connect() call. The second handler attempts to complete an already-completed Completer, causing a StateError: Bad state: Future already completed.

This is commonly seen in Flutter web apps using web_socket_client via pusher_client_socket, where the WebSocket connection is used for real-time communication.

Root Cause

The connect() function listens to both onOpen.first and onError.first with unawaited futures. On the web, a WebSocket can fire both events sequentially (open then error on disconnect), and the second event will crash because the Completer was already resolved by the first.

Fix

Add if (!completer.isCompleted) guards to both onOpen and onError handlers so that whichever event fires second is silently dropped instead of throwing StateError.

Before

socket.onOpen.first.then((_) {
  completer.complete(socket);
});

socket.onError.first.then((event) {
  completer.completeError(error);
});

After

socket.onOpen.first.then((_) {
  if (!completer.isCompleted) completer.complete(socket);
});

socket.onError.first.then((event) {
  if (!completer.isCompleted) {
    completer.completeError(error);
  }
});

Impact

  • Eliminates StateError: Bad state: Future already completed noise in production error tracking
  • No behavioral change: the first event still wins, the second is silently dropped
  • Backward compatible: no API changes

When a WebSocket opens successfully but later encounters an error,
both onOpen and onError fire on the same connect() call. The second
handler attempts to complete an already-completed Completer, causing
a StateError: Bad state: Future already completed.

Add isCompleted guards to both handlers to prevent the race.

Fixes the scenario where a WebSocket connection succeeds (onOpen
fires first, completing the completer) and then disconnects (onError
fires, trying to completeError an already-completed completer).
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.

1 participant