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, anint, such as3055.name: The name of the error, such asERROR_OPERATION_IN_PROGRESS.message: A description. The server can replace it with its own text, so checkcodeornamein your code, notmessage.
Common codes
| Code | Name | When | What to do |
|---|---|---|---|
| 3035 | ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED | The room has not joined yet, or it has left. | Call actions from the roomJoined handler or later. |
| 3054 | ERROR_ALREADY_IN_REQUESTED_STATE | There is nothing to do, for example leave() after the room left. | You can usually ignore it. |
| 3055 | ERROR_OPERATION_IN_PROGRESS | The same action is still running, for example after a double tap. | Disable the control until the first call completes. |
| 3065 | ERROR_ROOM_RECONNECTING | The room is reconnecting. | Try again after it reconnects. |
| 3073 | ERROR_INVALID_PARAMETER | An argument is not valid, for example an empty room id. | Fix the argument. |
| 4000 | UNKNOWN_ERROR | Most 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
PubSubandRealtimeStorecalls made before the room is joined throw aVideoSDKErrorwith code3035. Once the room is joined, a refused call throws aVideoSDKError.room.setWebcamQuality(),participant.setQuality()andparticipant.setScreenShareQuality()take aVideoQuality, so an unknown quality does not compile.- The pre-call test fails with a
PreCallTestException, whosecodeis aString. 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

