@@ -456,18 +456,25 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
456456 }
457457
458458 async cleanupPeerConnections ( ) {
459- await this . pcManager ?. close ( ) ;
460- this . pcManager = undefined ;
461-
462459 const dcCleanup = ( dc : RTCDataChannel | undefined ) => {
463- if ( ! dc ) return ;
464- dc . close ( ) ;
460+ if ( ! dc ) {
461+ return ;
462+ }
463+
464+ // Detach the data channel handlers before closing anything. Closing a peer connection tears
465+ // down the SCTP transport, which can dispatch `error`/`close` events on the still-open data
466+ // channels; if our handlers are still attached at that point, handleDataError logs a spurious
467+ // "Unknown DataChannel error" during an otherwise graceful disconnect. Removing the handlers
468+ // before dc.close()/pcManager.close() makes this deterministic regardless of how/when the
469+ // browser dispatches those teardown events. See livekit/client-sdk-js#1953.
465470 dc . onbufferedamountlow = null ;
466471 dc . onclose = null ;
467472 dc . onclosing = null ;
468473 dc . onerror = null ;
469474 dc . onmessage = null ;
470475 dc . onopen = null ;
476+
477+ dc . close ( ) ;
471478 } ;
472479 dcCleanup ( this . lossyDC ) ;
473480 dcCleanup ( this . lossyDCSub ) ;
@@ -476,6 +483,9 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
476483 dcCleanup ( this . dataTrackDC ) ;
477484 dcCleanup ( this . dataTrackDCSub ) ;
478485
486+ await this . pcManager ?. close ( ) ;
487+ this . pcManager = undefined ;
488+
479489 this . lossyDC = undefined ;
480490 this . lossyDCSub = undefined ;
481491 this . reliableDC = undefined ;
@@ -1038,12 +1048,24 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit
10381048 } ;
10391049
10401050 private handleDataError = ( event : Event ) => {
1051+ // Errors fired while we're tearing the connection down (e.g. the SCTP transport aborting as
1052+ // the peer connection closes) carry no actionable information — the channel is going away
1053+ // regardless. Suppress them so a graceful disconnect doesn't surface spurious errors.
1054+ // See livekit/client-sdk-js#1953.
1055+ if ( this . _isClosed ) {
1056+ return ;
1057+ }
1058+
10411059 const channel = event . currentTarget as RTCDataChannel ;
10421060 const channelKind = channel . maxRetransmits === 0 ? 'lossy' : 'reliable' ;
10431061
1044- if ( event instanceof ErrorEvent && event . error ) {
1045- const { error } = event . error ;
1046- this . log . error ( `DataChannel error on ${ channelKind } : ${ event . message } ` , { error } ) ;
1062+ if ( typeof RTCErrorEvent !== 'undefined' && event instanceof RTCErrorEvent && event . error ) {
1063+ const { error } = event ;
1064+ this . log . error ( `DataChannel error on ${ channelKind } : ${ error . message } ` , {
1065+ error,
1066+ errorDetail : error . errorDetail ,
1067+ sctpCauseCode : error . sctpCauseCode ,
1068+ } ) ;
10471069 } else {
10481070 this . log . error ( `Unknown DataChannel error on ${ channelKind } ` , { event } ) ;
10491071 }
0 commit comments