From 5a796ecbed57c0c89569308818e69221d71e8874 Mon Sep 17 00:00:00 2001 From: Ryan Gaus Date: Fri, 30 Oct 2026 12:00:44 +1300 Subject: [PATCH] fix: don't crash on malformed agent attributes Participant.updateFromInfo parsed lk.agent.inputs / lk.agent.outputs as JSON for every participant without catching errors. A value that wasn't a JSON array threw out of the signal handling coroutine and crashed the app. Catch and log the parse failure, leaving the previous agent attributes in place. Co-Authored-By: Claude Opus 5.5 --- .../malformed-agent-attributes-crash.md | 5 +++++ .../android/room/participant/Participant.kt | 7 ++++++- .../livekit/android/room/RoomMockE2ETest.kt | 21 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .changeset/malformed-agent-attributes-crash.md diff --git a/.changeset/malformed-agent-attributes-crash.md b/.changeset/malformed-agent-attributes-crash.md new file mode 100644 index 000000000..8c61cf964 --- /dev/null +++ b/.changeset/malformed-agent-attributes-crash.md @@ -0,0 +1,5 @@ +--- +"client-sdk-android": patch +--- + +Fix a crash when a participant's `lk.agent.inputs` or `lk.agent.outputs` attribute is not a valid JSON array. The attribute is now logged as a warning and the participant's agent attributes are left unchanged. diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Participant.kt b/livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Participant.kt index 81ac71543..388b8e497 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Participant.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/room/participant/Participant.kt @@ -31,6 +31,7 @@ import io.livekit.android.room.track.TrackPublication import io.livekit.android.room.types.AgentAttributes import io.livekit.android.room.types.fromStringMap import io.livekit.android.util.FlowObservable +import io.livekit.android.util.LKLog import io.livekit.android.util.diffMapChange import io.livekit.android.util.flow import io.livekit.android.util.flowDelegate @@ -459,7 +460,11 @@ open class Participant( permissions = ParticipantPermission.fromProto(info.permission) } attributes = info.attributesMap - agentAttributes = AgentAttributes.fromStringMap(info.attributesMap) + try { + agentAttributes = AgentAttributes.fromStringMap(info.attributesMap) + } catch (e: Exception) { + LKLog.w(e) { "Failed to parse agent attributes for participant ${info.identity}" } + } state = State.fromProto(info.state) clientProtocol = info.clientProtocol capabilities = info.capabilitiesList.mapNotNull { ClientCapability.fromProto(it) } diff --git a/livekit-android-test/src/test/java/io/livekit/android/room/RoomMockE2ETest.kt b/livekit-android-test/src/test/java/io/livekit/android/room/RoomMockE2ETest.kt index 0909ea41c..3eb74af16 100644 --- a/livekit-android-test/src/test/java/io/livekit/android/room/RoomMockE2ETest.kt +++ b/livekit-android-test/src/test/java/io/livekit/android/room/RoomMockE2ETest.kt @@ -178,6 +178,27 @@ class RoomMockE2ETest : MockE2ETest() { ) } + @Test + fun participantConnectedWithMalformedAgentAttributes() = runTest { + connect() + + val participantJoin = with(TestData.PARTICIPANT_JOIN.toBuilder()) { + update = with(update.toBuilder()) { + setParticipants( + 0, + TestData.REMOTE_PARTICIPANT.toBuilder() + .putAttributes("lk.agent.inputs", "not json") + .build(), + ) + build() + } + build() + } + simulateMessageFromServer(participantJoin) + + assertNotNull(room.getParticipantBySid(TestData.REMOTE_PARTICIPANT.sid)) + } + @Test fun participantSubscribesRemoteTrack() = runTest { connect()