Skip to main content
Version: 5.x.x

Error Handling - Flutter

Room, Participant and RTCStream actions, such as join(), unmuteMic() or pin(), return a Future that throws a VideoSDKError when the SDK refuses the action or the action fails. This page shows how to catch these errors and which codes to expect.

For errors that the room reports by itself, such as a rejected token or a lost connection, see Error Events.

Await every action​

Await each action inside try/catch. If you do not await it, a failure becomes an unhandled async error, and your code never sees it.

In a widget callback, make the callback async:

import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';

class UnmuteButton extends StatelessWidget {
const UnmuteButton({super.key, required this.room});

final Room room;

@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
try {
await room.unmuteMic();
} on VideoSDKError catch (error) {
debugPrint("Could not unmute: ${error.code} ${error.name}");
}
},
child: const Text("Unmute"),
);
}
}

VideoSDK.createCameraVideoTrack(), VideoSDK.createMicrophoneAudioTrack() and VideoSDK.createScreenShareVideoTrack() throw a VideoSDKError too, so catch it where you call them.

Read the error​

A VideoSDKError has three properties:

  • code: The error code, an int, such as 3055.
  • name: The name of the error, such as ERROR_OPERATION_IN_PROGRESS.
  • message: A description. The server can replace it with its own text, so check code or name in your code, not message.

Common codes​

CodeNameWhenWhat to do
3035ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINEDThe room has not joined yet, or it has left.Call actions from the roomJoined handler or later.
3054ERROR_ALREADY_IN_REQUESTED_STATEThere is nothing to do, for example leave() after the room left.You can usually ignore it.
3055ERROR_OPERATION_IN_PROGRESSThe same action is still running, for example after a double tap.Disable the control until the first call completes.
3065ERROR_ROOM_RECONNECTINGThe room is reconnecting.Try again after it reconnects.
3073ERROR_INVALID_PARAMETERAn argument is not valid, for example an empty room id.Fix the argument.
4000UNKNOWN_ERRORMost Room actions called before the first join(), or a failure that the SDK could not classify.Call join() first. Otherwise, log message.

Many actions also have codes of their own, for example 3017 when createCameraVideoTrack() has no camera permission. Room Error Codes lists every code.

The example below disables a button while its action runs, so a second tap cannot cause 3055:

import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';

class CameraButton extends StatefulWidget {
const CameraButton({super.key, required this.room});

final Room room;

@override
State<CameraButton> createState() => _CameraButtonState();
}

class _CameraButtonState extends State<CameraButton> {
bool _busy = false;

Future<void> _turnOnCamera() async {
setState(() => _busy = true);
try {
await widget.room.enableCam();
} on VideoSDKError catch (error) {
if (error.code == 3065) {
debugPrint("Reconnecting. Try again in a moment.");
} else if (error.code != 3054) {
debugPrint("Could not turn on the camera: ${error.code} ${error.name}");
}
} finally {
if (mounted) setState(() => _busy = false);
}
}

@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: _busy ? null : _turnOnCamera,
child: const Text("Turn on camera"),
);
}
}

Errors that also reach the error event​

Some failures reach both your catch block and the error event of the room, for example a failed room.send() (3070). Handle each error in one place, for example by leaving the codes that your catch blocks handle out of your error handler.

On Android, most actions called while the room reconnects report 3065 on the error event and return without throwing, so keep an error handler as well.

Other exceptions​

  • PubSub and RealtimeStore calls made before the room is joined throw a VideoSDKError with code 3035. Once the room is joined, a refused call throws a VideoSDKError.
  • room.setWebcamQuality(), participant.setQuality() and participant.setScreenShareQuality() take a VideoQuality, so an unknown quality does not compile.
  • The pre-call test fails with a PreCallTestException, whose code is a String. See Pre-call test errors.

Clean up without awaiting​

In State.dispose(), you cannot await. Add a catchError() handler to each call, so that a refusal does not become an unhandled error:

import 'package:flutter/material.dart';
import 'package:videosdk/videosdk.dart';

class RoomScreen extends StatefulWidget {
const RoomScreen({super.key, required this.room});

final Room room;

@override
State<RoomScreen> createState() => _RoomScreenState();
}

class _RoomScreenState extends State<RoomScreen> {
@override
void dispose() {
// The room releases itself after it leaves.
widget.room
.leave()
.catchError((Object error) => debugPrint("Leave failed: $error"));
super.dispose();
}

@override
Widget build(BuildContext context) => const SizedBox();
}

leave() throws 3054 when the room has already left, for example after another participant ended the room with end(). The catchError() handler covers that case too.

API Reference​

The API references for all the classes and events utilized in this guide are provided below.

Got a Question? Ask us on discord