Skip to content

Unhandled promise rejection: RTCEngine's onMediaSectionsRequirement handler calls negotiate() without a catch #2134

Closed

Description

@bawadev

Describe the bug

In RTCEngine.setupSignalClientCallbacks(), the onMediaSectionsRequirement handler fires off negotiate() without handling its promise:

this.client.onMediaSectionsRequirement = requirement => {
  this.addMediaSections(requirement.numAudios, requirement.numVideos);
  this.negotiate();            // <- returned promise is never caught
};

negotiate() rejects with a NegotiationError in normal operating conditions, e.g. negotiation timed out on a poor network, or PC manager is closed during teardown. Here that rejection becomes an unhandled promise rejection.

Every other internal caller already catches it:

this.negotiate().catch(err => { this.log.error(err); });

negotiate() already starts the disconnect/reconnect flow on failure, so only the stray promise is the problem. In React Native it surfaces as a global onunhandledrejection, which crash reporters show as an error with no app frames. We saw it in production as NegotiationError: negotiation timed out from users on weak Wi-Fi.

Reproduction

Node 20+, against the published ESM bundle:

let unhandled = 0;
process.on('unhandledRejection', (e) => { unhandled++; console.log('unhandled:', e?.name, e?.message); });
const lk = await import('livekit-client/dist/livekit-client.esm.mjs');
const engine = new lk.Room().engine;
engine.setupSignalClientCallbacks();
engine.addMediaSections = () => {};
engine.pcManager = undefined;                 // negotiate() rejects: NegotiationError('PC manager is closed')
engine.client.onMediaSectionsRequirement({ numAudios: 1, numVideos: 0 });
await new Promise((r) => setTimeout(r, 200));
console.log('unhandled rejections =', unhandled); // 1

With a .catch() on that call, the count is 0.

Expected behavior

The rejection is caught and logged, like the other negotiate() call sites.

Suggested fix

 this.client.onMediaSectionsRequirement = requirement => {
   this.addMediaSections(requirement.numAudios, requirement.numVideos);
-  this.negotiate();
+  this.negotiate().catch(err => {
+    this.log.warn('negotiation for new media sections failed', { ...this.logContext, error: err });
+  });
 };

Environment

Activity

  1. 1egoman commented on Oct 8, 2026

    @1egoman
    Contributor

    This was fixed in #2108, this will be released in the next release which should be in the next few days (#2091)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions