@@ -1106,12 +1106,15 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
11061106 unawaited (handleReconnect (ClientDisconnectReason .reconnectRetry));
11071107 } else {
11081108 logger.fine ('attemptReconnect: disconnecting...' );
1109+ // clean up before emitting, room's EngineDisconnectedEvent handler
1110+ // drops the event while fullReconnectOnNext is still true and
1111+ // cleanUp() is what resets it
1112+ await cleanUp ();
11091113 events.emit (EngineDisconnectedEvent (
11101114 reason: e is CertificatePinningException
11111115 ? DisconnectReason .signalingConnectionFailure
11121116 : DisconnectReason .disconnected,
11131117 ));
1114- await cleanUp ();
11151118 }
11161119 } finally {
11171120 _attemptingReconnect = false ;
@@ -1365,9 +1368,16 @@ class Engine extends Disposable with EventsEmittable<EngineEvent> {
13651368 await handleReconnect (ClientDisconnectReason .signal,
13661369 reconnectReason: lk_models.ReconnectReason .RR_SIGNAL_DISCONNECTED );
13671370 } else if (event.reason == DisconnectReason .signalingConnectionFailure) {
1368- events.emit (EngineDisconnectedEvent (
1369- reason: event.reason,
1370- ));
1371+ // while reconnecting, attemptReconnect owns disconnect handling and
1372+ // emits EngineDisconnectedEvent itself, relaying here as well would
1373+ // race it with a duplicate event. _attemptingReconnect covers the
1374+ // window where cleanUp() has already reset _isReconnecting but
1375+ // attemptReconnect has not finished its error handling yet
1376+ if (! _isReconnecting && ! _attemptingReconnect) {
1377+ events.emit (EngineDisconnectedEvent (
1378+ reason: event.reason,
1379+ ));
1380+ }
13711381 }
13721382 })
13731383 ..on < SignalOfferEvent > ((event) async {
0 commit comments