From baf646ebcdbd550d40dbaf5afa143b6cf21beac1 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Fri, 7 Aug 2026 20:04:04 +0900 Subject: [PATCH 1/3] Surface newer server disconnect reasons as distinct DisconnectReason values The protocol defines nine disconnect reasons the public enum had no members for, so they were all reported as unknown. Add members for them and map them in toSDKType. --- .changes/disconnect-reason-members | 1 + lib/src/extensions.dart | 9 +++++ lib/src/types/other.dart | 9 +++++ test/types/disconnect_reason_test.dart | 48 ++++++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 .changes/disconnect-reason-members create mode 100644 test/types/disconnect_reason_test.dart diff --git a/.changes/disconnect-reason-members b/.changes/disconnect-reason-members new file mode 100644 index 000000000..1cb678d6d --- /dev/null +++ b/.changes/disconnect-reason-members @@ -0,0 +1 @@ +minor type="added" "DisconnectReason gained members for the newer server reasons (roomClosed, migration, signalClose, userUnavailable, userRejected, sipTrunkFailure, connectionTimeout, mediaFailure, agentError), 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..ecbf1ea0f 100644 --- a/lib/src/types/other.dart +++ b/lib/src/types/other.dart @@ -161,6 +161,15 @@ enum DisconnectReason { disconnected, signalingConnectionFailure, reconnectAttemptsExceeded, + migration, + signalClose, + roomClosed, + userUnavailable, + userRejected, + sipTrunkFailure, + connectionTimeout, + mediaFailure, + 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); + }); + }); +} From 817f60188239eabc275804a0e44eceed08af3a29 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Fri, 7 Aug 2026 20:32:39 +0900 Subject: [PATCH 2/3] Shorten changeset --- .changes/disconnect-reason-members | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changes/disconnect-reason-members b/.changes/disconnect-reason-members index 1cb678d6d..dc644d4f4 100644 --- a/.changes/disconnect-reason-members +++ b/.changes/disconnect-reason-members @@ -1 +1 @@ -minor type="added" "DisconnectReason gained members for the newer server reasons (roomClosed, migration, signalClose, userUnavailable, userRejected, sipTrunkFailure, connectionTimeout, mediaFailure, agentError), previously reported as unknown" +minor type="added" "New DisconnectReason members for newer server disconnect reasons, previously reported as unknown" From 47944ee063c48e27d79e2ea3355b591e5c693423 Mon Sep 17 00:00:00 2001 From: Hiroshi Horie <548776+hiroshihorie@users.noreply.github.com> Date: Sat, 8 Aug 2026 15:56:23 +0900 Subject: [PATCH 3/3] Document DisconnectReason members Ported from the protocol's proto comments, with the client side only members called out explicitly and the roomClosed versus roomDeleted distinction spelled out. --- lib/src/types/other.dart | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/lib/src/types/other.dart b/lib/src/types/other.dart index ecbf1ea0f..6ccc7d53f 100644 --- a/lib/src/types/other.dart +++ b/lib/src/types/other.dart @@ -149,26 +149,77 @@ 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, }