Skip to main content
Version: 1.x.x

Migration Guide

1. Promise-based API

All methods on VideoSDK, Meeting, Participant, Stream, and other SDK classes are asynchronous and return a Promise. Always await them.

  • If a method fails, it throws an error that you should handle with try / catch.
  • The global error listener (meeting.on("error", ...)) continues to receive the same errors. Use it for centralized error handling, while try / catch lets you handle errors specific to an individual method call.

Before - fire-and-forget call; errors arrived only on the global error event

meeting.enableMic();

meeting.on("error", (err) => {
console.error(err.code, err.message);
});

After - errors are thrown from the awaited call (and also emitted on meeting.on("error", …))

try {
await meeting.enableMic();
console.log("mic on");
} catch (err) {
console.error(err.name, err.code, err.message);
}

// The global listener still fires for the same errors
meeting.on("error", (err) => {
console.error(err.code, err.message);
});

2. PubSub

2.1 subscribe now takes a listener object and returns Promise<void>

Before

const history = await meeting.pubSub.subscribe("CHAT", (msg) => {
console.log(msg);
});
console.log(history);

After

// Create the listeners object once - reuse it for both subscribe and unsubscribe
const chatListeners = {
onMessageReceived: (msg) => console.log("live:", msg),
onOldMessagesReceived: (msgs, info) => console.log("history:", msgs),
onMessageDrop: (info) => console.warn("dropped:", info),
};

const chatOptions = {
oldMessageLimit: 50, // how many stored messages to fetch on subscribe
newMessageLimit: 20, // max realtime messages received per 500 ms
realtimeOverflow: "queue", // "queue" | "drop"
maxQueue: 100, // only valid when realtimeOverflow === "queue"; default 70, max 200
};

await meeting.pubSub.subscribe("CHAT", chatListeners, chatOptions);

Key points:

  • subscribe returns Promise<void>. Historical messages are delivered only via onOldMessagesReceived.
  • The second argument is now a listener object, not a bare function.
  • The third argument is an options object (batching / history / backpressure).
  • Keep references to each callback - you'll pass them to unsubscribe to remove them selectively.

2.2 unsubscribe takes a listener object with only the callbacks you want to remove

unsubscribe accepts a listener object describing which callbacks to remove. Include only the callbacks you want to unsubscribe - the rest stay active. For example, if you subscribed with 4 callbacks and pass 2 in unsubscribe, only those 2 are removed; the other 2 continue to receive messages.

Before

await meeting.pubSub.unsubscribe("CHAT", callback);

After - remove all listeners

await meeting.pubSub.unsubscribe("CHAT", chatListeners);

After - remove some listeners

// Only `onMessageDrop` and `onOldMessagesReceived` are removed.
// The other callbacks in `chatListeners` remain subscribed.
await meeting.pubSub.unsubscribe("CHAT", {
onMessageDrop: chatListeners.onMessageDrop,
onOldMessagesReceived: chatListeners.onOldMessagesReceived,
});

Key points:

  • subscribe signature is now three positional arguments: (topic, listeners, options). Previously it was (topic, callback).
  • The second argument is a listeners object, not a bare function. New listeners: onBatchReceived, onMessageDrop, onOldMessagesReceived.
  • The third argument is an options object (batching / history / backpressure): oldMessageLimit, newMessageLimit, realtimeOverflow, maxQueue.
  • subscribe returns Promise<void>. Historical messages are delivered only via onOldMessagesReceived.
  • unsubscribe accepts a listeners object with only the callbacks you want to remove — keep references to each callback at subscribe time.
  • Learn more: Chat using PubSub

3. Track configuration is preserved

When you pass a media track to the SDK, its configuration is stored and reused automatically.

In v0.x.x, disabling and re-enabling a track-or switching modes with changeMode()-reset the track configuration to the SDK default. Starting with v1.0.0, the SDK preserves the most recently configured track and reuses it whenever the track is enabled again, including after transitioning back to a producing mode with changeMode().

The stored track can come from:

  • The initial configuration provided to initMeeting().
  • A later call to enableMic() / enableWebcam() with a custom track.
  • A later call to changeMic() / changeWebcam() with a custom track (see below).

changeMic() / changeWebcam() accept either a deviceId or a customTrack:

  • changeMic(deviceId) / changeWebcam(deviceId) - only the input device is swapped. All other settings from the stored config (encoderConfig, bitrateMode, multiStream, maxLayer, noiseConfig, etc.) are preserved.
  • changeMic(customTrack) / changeWebcam(customTrack) - the entire track replaces the stored one. The new track becomes the "last config" and is reused on every subsequent enable and mode change.

Before

const camTrack = await VideoSDK.createCameraVideoTrack({
encoderConfig: "h540p_w960p",
});

const meeting = VideoSDK.initMeeting({
meetingId,
customCameraVideoTrack: camTrack,
webcamEnabled: true,
});

await meeting.disableWebcam();
await meeting.enableWebcam(); // ❌ default track (h720p_w1280p) - h540p_w960p dropped

await meeting.changeMode(VideoSDK.Constants.modes.RECV_ONLY);
await meeting.changeMode(VideoSDK.Constants.modes.SEND_AND_RECV); // ❌ default track (h720p_w1280p) - h540p_w960p dropped

After

const camTrack = await VideoSDK.createCameraVideoTrack({
encoderConfig: "h540p_w960p",
});

const meeting = VideoSDK.initMeeting({
meetingId,
customCameraVideoTrack: camTrack,
webcamEnabled: true,
});

await meeting.disableWebcam();
await meeting.enableWebcam(); // ✅ reuses h540p_w960p

// Provide a different track later - its config becomes the new "last config"
const camTrack2 = await VideoSDK.createCameraVideoTrack({
encoderConfig: "h480p_w640p",
});
await meeting.enableWebcam(camTrack2);

await meeting.changeMode(VideoSDK.Constants.modes.RECV_ONLY);
await meeting.changeMode(VideoSDK.Constants.modes.SEND_AND_RECV); // ✅ reuses h480p_w640p

4. Behavior changes

4.1 Concurrent media and mode operations

enableMic(), disableMic(), enableWebcam(), disableWebcam(), and changeMode() now reject overlapping operations.

In v0.x.x, rapid user interactions or concurrent calls could race with one another, leaving the meeting or media state inconsistent. Starting with v1.0.0, the first operation proceeds normally, while any overlapping call throws the corresponding ERROR_OPERATION_IN_PROGRESS.

What you should do

  • Prevent overlapping operations in your application. For example, disable your mic, webcam, and mode controls while an operation is in progress, and don't start another operation until the previous one completes.

4.2 Track creation methods now throw on failure

createCameraVideoTrack() and createMicrophoneAudioTrack() now throw on failure instead of returning null.

This affects failures such as:

  • Permission denied
  • No input device
  • Invalid configuration
  • Unsupported codec

Before

const track = await VideoSDK.createCameraVideoTrack();
console.log(track); // null

After

try {
const track = await VideoSDK.createCameraVideoTrack();
} catch (err) {
// Handle permission, missing device, invalid config, etc.
showError(err.message);
}

4.3 Media controls are only available in SEND_AND_RECV mode

enableMic(), disableMic(), enableWebcam(), and disableWebcam() can only be called when the meeting is in SEND_AND_RECV mode, which supports publishing media.

Starting with v1.0.0, these methods throw an error if the current meeting mode is not SEND_AND_RECV.

What you should do

Switch the meeting to SEND_AND_RECV before calling these methods:

await meeting.changeMode(VideoSDK.Constants.modes.SEND_AND_RECV);

4.4 RealtimeStore.getValue() throws when a key doesn't exist

Previously, calling getValue() on an unset key returned null.

Starting with v1.0.0, getValue() throws FAILED_TO_GET_VALUE for unset keys. Update your code to treat this exception as the equivalent of a missing value.


4.5 All methods are now guarded before the meeting is joined

In v0.x.x, calling methods before the meeting was joined was inconsistent — some methods returned with an error on meeting.on("error", …), while others silently no-op'd or produced undefined behavior.

Starting with v1.0.0, every method on Meeting, Participant, Stream, and pubSub throws ERROR_ACTION_PERFORMED_BEFORE_MEETING_JOINED if called before the meeting-joined event has fired. Wait for meeting-joined before invoking any method.

Got a Question? Ask us on discord