From 2fa4c83e995cf555d065af315c10849e5a219f37 Mon Sep 17 00:00:00 2001 From: Herman Bilous Date: Tue, 5 Mar 2024 17:44:03 +0200 Subject: [PATCH 1/4] Catch errors when setRemoteDescription or addIceCandidate on peer connection fails --- src/room/RTCEngine.ts | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/room/RTCEngine.ts b/src/room/RTCEngine.ts index 240e4a7a61..e9454b71a4 100644 --- a/src/room/RTCEngine.ts +++ b/src/room/RTCEngine.ts @@ -447,7 +447,13 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit return; } this.log.debug('received server answer', { ...this.logContext, RTCSdpType: sd.type }); - await this.pcManager.setPublisherAnswer(sd); + + try { + await this.pcManager.setPublisherAnswer(sd); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'unknown error' + log.error(`failed to setPublisherAnswer: ${errorMessage}`, { ...this.logContext, RTCSdpType: sd.type }) + } }; // add candidate on trickle @@ -456,7 +462,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit return; } this.log.trace('got ICE candidate from peer', { ...this.logContext, candidate, target }); - this.pcManager.addIceCandidate(candidate, target); + this.pcManager.addIceCandidate(candidate, target).catch(error => { + log.error(`failed to addIceCandidate: ${error.message}`, { + ...this.logContext, candidate, target + }) + }); }; // when server creates an offer for the client @@ -464,8 +474,16 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit if (!this.pcManager) { return; } - const answer = await this.pcManager.createSubscriberAnswerFromOffer(sd); - this.client.sendAnswer(answer); + + try { + const answer = await this.pcManager.createSubscriberAnswerFromOffer(sd); + await this.client.sendAnswer(answer); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'unknown error' + log.error(`failed to createSubscriberAnswerFromOffer: ${errorMessage}`, { + ...this.logContext + }) + } }; this.client.onLocalTrackPublished = (res: TrackPublishedResponse) => { From cd7d13f0e11714759cf80743988eaba93cc7e1a5 Mon Sep 17 00:00:00 2001 From: Herman Bilous Date: Tue, 5 Mar 2024 17:47:14 +0200 Subject: [PATCH 2/4] Create fast-seahorses-juggle.md --- .changeset/fast-seahorses-juggle.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fast-seahorses-juggle.md diff --git a/.changeset/fast-seahorses-juggle.md b/.changeset/fast-seahorses-juggle.md new file mode 100644 index 0000000000..687cd2c966 --- /dev/null +++ b/.changeset/fast-seahorses-juggle.md @@ -0,0 +1,5 @@ +--- +"livekit-client": patch +--- + +Catch errors when peer connection signaling state is closed From 8b828b6d5f84a74f7d839adcf0b65369b317e594 Mon Sep 17 00:00:00 2001 From: Herman Bilous Date: Tue, 5 Mar 2024 17:51:04 +0200 Subject: [PATCH 3/4] Fix prettier --- src/room/RTCEngine.ts | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/room/RTCEngine.ts b/src/room/RTCEngine.ts index e9454b71a4..6c54ff4d90 100644 --- a/src/room/RTCEngine.ts +++ b/src/room/RTCEngine.ts @@ -451,8 +451,11 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit try { await this.pcManager.setPublisherAnswer(sd); } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'unknown error' - log.error(`failed to setPublisherAnswer: ${errorMessage}`, { ...this.logContext, RTCSdpType: sd.type }) + const errorMessage = error instanceof Error ? error.message : 'unknown error'; + log.error(`failed to setPublisherAnswer: ${errorMessage}`, { + ...this.logContext, + RTCSdpType: sd.type, + }); } }; @@ -462,10 +465,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit return; } this.log.trace('got ICE candidate from peer', { ...this.logContext, candidate, target }); - this.pcManager.addIceCandidate(candidate, target).catch(error => { + this.pcManager.addIceCandidate(candidate, target).catch((error) => { log.error(`failed to addIceCandidate: ${error.message}`, { - ...this.logContext, candidate, target - }) + ...this.logContext, + candidate, + target, + }); }); }; @@ -479,10 +484,10 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit const answer = await this.pcManager.createSubscriberAnswerFromOffer(sd); await this.client.sendAnswer(answer); } catch (error) { - const errorMessage = error instanceof Error ? error.message : 'unknown error' + const errorMessage = error instanceof Error ? error.message : 'unknown error'; log.error(`failed to createSubscriberAnswerFromOffer: ${errorMessage}`, { - ...this.logContext - }) + ...this.logContext, + }); } }; From 3e5e09647068ee962e9343bf8bd42dd7cb265166 Mon Sep 17 00:00:00 2001 From: Herman Bilous Date: Tue, 5 Mar 2024 19:05:18 +0200 Subject: [PATCH 4/4] Add reconnect when InvalidStateError happens --- src/room/RTCEngine.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/room/RTCEngine.ts b/src/room/RTCEngine.ts index 6c54ff4d90..ed2bb68d52 100644 --- a/src/room/RTCEngine.ts +++ b/src/room/RTCEngine.ts @@ -441,6 +441,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit } private setupSignalClientCallbacks() { + const tryHandleInvalidStateError = (error: unknown, disconnectReason: ReconnectReason) => { + if (error instanceof DOMException && error.name === 'InvalidStateError') { + this.fullReconnectOnNext = true; + this.handleDisconnect('peerconnection failed', disconnectReason); + } + }; // configure signaling client this.client.onAnswer = async (sd) => { if (!this.pcManager) { @@ -456,6 +462,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit ...this.logContext, RTCSdpType: sd.type, }); + tryHandleInvalidStateError(error, ReconnectReason.RR_PUBLISHER_FAILED); } }; @@ -471,6 +478,12 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit candidate, target, }); + tryHandleInvalidStateError( + error, + target === SignalTarget.PUBLISHER + ? ReconnectReason.RR_PUBLISHER_FAILED + : ReconnectReason.RR_SUBSCRIBER_FAILED, + ); }); }; @@ -488,6 +501,7 @@ export default class RTCEngine extends (EventEmitter as new () => TypedEventEmit log.error(`failed to createSubscriberAnswerFromOffer: ${errorMessage}`, { ...this.logContext, }); + tryHandleInvalidStateError(error, ReconnectReason.RR_SUBSCRIBER_FAILED); } };