Skip to content

Commit f24c1ea

Browse files
authored
Surface newer server disconnect reasons as distinct DisconnectReason values (#1158)
Follow-up to #1156 (replaces #1157, which GitHub auto-closed when the stack base merged). The protocol defines 17 disconnect reasons but the public `DisconnectReason` enum only had members for 8 of them, so newer reasons arriving in a leave request (for example when the server closes a room or a SIP trunk fails) were all collapsed to `unknown`. This adds members for the nine missing reasons and maps them in `toSDKType`: `migration`, `signalClose`, `roomClosed`, `userUnavailable`, `userRejected`, `sipTrunkFailure`, `connectionTimeout`, `mediaFailure`, `agentError` Unrecognized values from servers newer than the SDK still fall back to `unknown` via the wildcard arm introduced in #1156. Note for apps: adding enum members means an exhaustive `switch` over `DisconnectReason` in app code will need new cases, which is why the changeset is `minor`. ## Tests New `test/types/disconnect_reason_test.dart` asserts every proto value maps to a distinct SDK value and pins the nine new mappings. Full suite passes (380 tests), analyze, format, and import sorter clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 96b8d8c commit f24c1ea

4 files changed

Lines changed: 118 additions & 0 deletions

File tree

.changes/disconnect-reason-members

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
minor type="added" "New DisconnectReason members for newer server disconnect reasons, previously reported as unknown"

lib/src/extensions.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,15 @@ extension DisconnectReasonExt on lk_models.DisconnectReason {
242242
lk_models.DisconnectReason.ROOM_DELETED => DisconnectReason.roomDeleted,
243243
lk_models.DisconnectReason.STATE_MISMATCH => DisconnectReason.stateMismatch,
244244
lk_models.DisconnectReason.JOIN_FAILURE => DisconnectReason.joinFailure,
245+
lk_models.DisconnectReason.MIGRATION => DisconnectReason.migration,
246+
lk_models.DisconnectReason.SIGNAL_CLOSE => DisconnectReason.signalClose,
247+
lk_models.DisconnectReason.ROOM_CLOSED => DisconnectReason.roomClosed,
248+
lk_models.DisconnectReason.USER_UNAVAILABLE => DisconnectReason.userUnavailable,
249+
lk_models.DisconnectReason.USER_REJECTED => DisconnectReason.userRejected,
250+
lk_models.DisconnectReason.SIP_TRUNK_FAILURE => DisconnectReason.sipTrunkFailure,
251+
lk_models.DisconnectReason.CONNECTION_TIMEOUT => DisconnectReason.connectionTimeout,
252+
lk_models.DisconnectReason.MEDIA_FAILURE => DisconnectReason.mediaFailure,
253+
lk_models.DisconnectReason.AGENT_ERROR => DisconnectReason.agentError,
245254
_ => DisconnectReason.unknown,
246255
};
247256
}

lib/src/types/other.dart

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,18 +149,78 @@ enum StreamState {
149149
active,
150150
}
151151

152+
/// The reason a participant was disconnected from the room.
153+
///
154+
/// Most values mirror the server's disconnect reasons from the LiveKit
155+
/// protocol. [disconnected], [signalingConnectionFailure] and
156+
/// [reconnectAttemptsExceeded] are produced by this SDK for client side
157+
/// conditions and are never sent by the server.
152158
enum DisconnectReason {
153159
unknown,
160+
161+
/// The client initiated the disconnect.
154162
clientInitiated,
163+
164+
/// Another participant with the same identity has joined the room.
155165
duplicateIdentity,
166+
167+
/// The server instance is shutting down.
156168
serverShutdown,
169+
170+
/// RoomService.RemoveParticipant was called.
157171
participantRemoved,
172+
173+
/// RoomService.DeleteRoom was called.
158174
roomDeleted,
175+
176+
/// The client attempted to resume a session, but the server is not aware
177+
/// of it.
159178
stateMismatch,
179+
180+
/// The client was unable to connect fully.
160181
joinFailure,
182+
183+
/// The signaling connection was closed. Client side, not sent by the
184+
/// server.
161185
disconnected,
186+
187+
/// The signaling connection could not be established. Client side, not
188+
/// sent by the server.
162189
signalingConnectionFailure,
190+
191+
/// The client gave up reconnecting after exhausting all attempts. Client
192+
/// side, not sent by the server.
163193
reconnectAttemptsExceeded,
194+
195+
/// Cloud only. The server requested the participant to migrate the
196+
/// connection elsewhere.
197+
migration,
198+
199+
/// The signal websocket was closed unexpectedly.
200+
signalClose,
201+
202+
/// The room was closed, due to all Standard and Ingress participants
203+
/// having left. Distinct from [roomDeleted], which is an explicit
204+
/// RoomService.DeleteRoom call.
205+
roomClosed,
206+
207+
/// SIP callee did not respond in time.
208+
userUnavailable,
209+
210+
/// SIP callee rejected the call (busy).
211+
userRejected,
212+
213+
/// SIP protocol failure or unexpected response.
214+
sipTrunkFailure,
215+
216+
/// The server timed out the participant session.
217+
connectionTimeout,
218+
219+
/// Media stream failure or media timeout.
220+
mediaFailure,
221+
222+
/// The agent encountered an error.
223+
agentError,
164224
}
165225

166226
/// The reason why a track failed to publish.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2026 LiveKit, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
import 'package:flutter_test/flutter_test.dart';
16+
17+
import 'package:livekit_client/src/extensions.dart';
18+
import 'package:livekit_client/src/proto/livekit_models.pb.dart' as lk_models;
19+
import 'package:livekit_client/src/types/other.dart';
20+
21+
void main() {
22+
group('DisconnectReason.toSDKType', () {
23+
test('maps every proto value to a distinct SDK value', () {
24+
final mapped = <DisconnectReason>{};
25+
for (final reason in lk_models.DisconnectReason.values) {
26+
final sdkReason = reason.toSDKType();
27+
expect(
28+
mapped.contains(sdkReason),
29+
isFalse,
30+
reason: '$reason maps to $sdkReason which is already used by another proto value',
31+
);
32+
mapped.add(sdkReason);
33+
}
34+
});
35+
36+
test('maps newer server reasons to their own members', () {
37+
expect(lk_models.DisconnectReason.ROOM_CLOSED.toSDKType(), DisconnectReason.roomClosed);
38+
expect(lk_models.DisconnectReason.MIGRATION.toSDKType(), DisconnectReason.migration);
39+
expect(lk_models.DisconnectReason.SIGNAL_CLOSE.toSDKType(), DisconnectReason.signalClose);
40+
expect(lk_models.DisconnectReason.USER_UNAVAILABLE.toSDKType(), DisconnectReason.userUnavailable);
41+
expect(lk_models.DisconnectReason.USER_REJECTED.toSDKType(), DisconnectReason.userRejected);
42+
expect(lk_models.DisconnectReason.SIP_TRUNK_FAILURE.toSDKType(), DisconnectReason.sipTrunkFailure);
43+
expect(lk_models.DisconnectReason.CONNECTION_TIMEOUT.toSDKType(), DisconnectReason.connectionTimeout);
44+
expect(lk_models.DisconnectReason.MEDIA_FAILURE.toSDKType(), DisconnectReason.mediaFailure);
45+
expect(lk_models.DisconnectReason.AGENT_ERROR.toSDKType(), DisconnectReason.agentError);
46+
});
47+
});
48+
}

0 commit comments

Comments
 (0)