diff --git a/.changes/disconnect-reason-members b/.changes/disconnect-reason-members new file mode 100644 index 000000000..dc644d4f4 --- /dev/null +++ b/.changes/disconnect-reason-members @@ -0,0 +1 @@ +minor type="added" "New DisconnectReason members for newer server disconnect reasons, previously reported as unknown" diff --git a/lib/src/extensions.dart b/lib/src/extensions.dart index a2886aedd..b4fb8b2e9 100644 --- a/lib/src/extensions.dart +++ b/lib/src/extensions.dart @@ -242,6 +242,15 @@ extension DisconnectReasonExt on lk_models.DisconnectReason { lk_models.DisconnectReason.ROOM_DELETED => DisconnectReason.roomDeleted, lk_models.DisconnectReason.STATE_MISMATCH => DisconnectReason.stateMismatch, lk_models.DisconnectReason.JOIN_FAILURE => DisconnectReason.joinFailure, + lk_models.DisconnectReason.MIGRATION => DisconnectReason.migration, + lk_models.DisconnectReason.SIGNAL_CLOSE => DisconnectReason.signalClose, + lk_models.DisconnectReason.ROOM_CLOSED => DisconnectReason.roomClosed, + lk_models.DisconnectReason.USER_UNAVAILABLE => DisconnectReason.userUnavailable, + lk_models.DisconnectReason.USER_REJECTED => DisconnectReason.userRejected, + lk_models.DisconnectReason.SIP_TRUNK_FAILURE => DisconnectReason.sipTrunkFailure, + lk_models.DisconnectReason.CONNECTION_TIMEOUT => DisconnectReason.connectionTimeout, + lk_models.DisconnectReason.MEDIA_FAILURE => DisconnectReason.mediaFailure, + lk_models.DisconnectReason.AGENT_ERROR => DisconnectReason.agentError, _ => DisconnectReason.unknown, }; } diff --git a/lib/src/types/other.dart b/lib/src/types/other.dart index 98cf6be0a..6ccc7d53f 100644 --- a/lib/src/types/other.dart +++ b/lib/src/types/other.dart @@ -149,18 +149,78 @@ enum StreamState { active, } +/// The reason a participant was disconnected from the room. +/// +/// Most values mirror the server's disconnect reasons from the LiveKit +/// protocol. [disconnected], [signalingConnectionFailure] and +/// [reconnectAttemptsExceeded] are produced by this SDK for client side +/// conditions and are never sent by the server. enum DisconnectReason { unknown, + + /// The client initiated the disconnect. clientInitiated, + + /// Another participant with the same identity has joined the room. duplicateIdentity, + + /// The server instance is shutting down. serverShutdown, + + /// RoomService.RemoveParticipant was called. participantRemoved, + + /// RoomService.DeleteRoom was called. roomDeleted, + + /// The client attempted to resume a session, but the server is not aware + /// of it. stateMismatch, + + /// The client was unable to connect fully. joinFailure, + + /// The signaling connection was closed. Client side, not sent by the + /// server. disconnected, + + /// The signaling connection could not be established. Client side, not + /// sent by the server. signalingConnectionFailure, + + /// The client gave up reconnecting after exhausting all attempts. Client + /// side, not sent by the server. reconnectAttemptsExceeded, + + /// Cloud only. The server requested the participant to migrate the + /// connection elsewhere. + migration, + + /// The signal websocket was closed unexpectedly. + signalClose, + + /// The room was closed, due to all Standard and Ingress participants + /// having left. Distinct from [roomDeleted], which is an explicit + /// RoomService.DeleteRoom call. + roomClosed, + + /// SIP callee did not respond in time. + userUnavailable, + + /// SIP callee rejected the call (busy). + userRejected, + + /// SIP protocol failure or unexpected response. + sipTrunkFailure, + + /// The server timed out the participant session. + connectionTimeout, + + /// Media stream failure or media timeout. + mediaFailure, + + /// The agent encountered an error. + agentError, } /// The reason why a track failed to publish. diff --git a/test/types/disconnect_reason_test.dart b/test/types/disconnect_reason_test.dart new file mode 100644 index 000000000..80168be03 --- /dev/null +++ b/test/types/disconnect_reason_test.dart @@ -0,0 +1,48 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import 'package:flutter_test/flutter_test.dart'; + +import 'package:livekit_client/src/extensions.dart'; +import 'package:livekit_client/src/proto/livekit_models.pb.dart' as lk_models; +import 'package:livekit_client/src/types/other.dart'; + +void main() { + group('DisconnectReason.toSDKType', () { + test('maps every proto value to a distinct SDK value', () { + final mapped = {}; + for (final reason in lk_models.DisconnectReason.values) { + final sdkReason = reason.toSDKType(); + expect( + mapped.contains(sdkReason), + isFalse, + reason: '$reason maps to $sdkReason which is already used by another proto value', + ); + mapped.add(sdkReason); + } + }); + + test('maps newer server reasons to their own members', () { + expect(lk_models.DisconnectReason.ROOM_CLOSED.toSDKType(), DisconnectReason.roomClosed); + expect(lk_models.DisconnectReason.MIGRATION.toSDKType(), DisconnectReason.migration); + expect(lk_models.DisconnectReason.SIGNAL_CLOSE.toSDKType(), DisconnectReason.signalClose); + expect(lk_models.DisconnectReason.USER_UNAVAILABLE.toSDKType(), DisconnectReason.userUnavailable); + expect(lk_models.DisconnectReason.USER_REJECTED.toSDKType(), DisconnectReason.userRejected); + expect(lk_models.DisconnectReason.SIP_TRUNK_FAILURE.toSDKType(), DisconnectReason.sipTrunkFailure); + expect(lk_models.DisconnectReason.CONNECTION_TIMEOUT.toSDKType(), DisconnectReason.connectionTimeout); + expect(lk_models.DisconnectReason.MEDIA_FAILURE.toSDKType(), DisconnectReason.mediaFailure); + expect(lk_models.DisconnectReason.AGENT_ERROR.toSDKType(), DisconnectReason.agentError); + }); + }); +}