@@ -821,11 +821,22 @@ def _on_room_event(self, event: proto_room.RoomEvent) -> None:
821821 rparticipant ._track_publications [rpublication .sid ] = rpublication
822822 self .emit ("track_published" , rpublication , rparticipant )
823823 elif which == "track_unpublished" :
824- rparticipant = self ._remote_participants [event .track_unpublished .participant_identity ]
825- rpublication = rparticipant ._track_publications .pop (
826- event .track_unpublished .publication_sid
827- )
828- self .emit ("track_unpublished" , rpublication , rparticipant )
824+ # The participant or publication may already have been removed by a
825+ # racing disconnect or a duplicate event, so both lookups are done
826+ # defensively and the emit is skipped when the entry is gone,
827+ # mirroring the local_track_unpublished handler, instead of raising a
828+ # KeyError that _listen_task logs as an error.
829+ identity = event .track_unpublished .participant_identity
830+ sid = event .track_unpublished .publication_sid
831+ rparticipant = self ._remote_participants .get (identity )
832+ if rparticipant is not None :
833+ rpublication = rparticipant ._track_publications .pop (sid , None )
834+ if rpublication is not None :
835+ self .emit ("track_unpublished" , rpublication , rparticipant )
836+ else :
837+ logging .debug ("track_unpublished for untracked publication sid %s" , sid )
838+ else :
839+ logging .debug ("track_unpublished for untracked participant %s" , identity )
829840 elif which == "track_subscribed" :
830841 owned_track_info = event .track_subscribed .track
831842 track_info = owned_track_info .info
0 commit comments