@@ -746,9 +746,20 @@ def _on_room_event(self, event: proto_room.RoomEvent) -> None:
746746 ltrack = lpublication .track
747747 self .emit ("local_track_published" , lpublication , ltrack )
748748 elif which == "local_track_unpublished" :
749+ # During teardown the publication may already have been removed
750+ # from the participant's dict by LocalParticipant.unpublish_track
751+ # (the FFI event races that async response), so the SID can be gone
752+ # by the time this event is dispatched. Look it up defensively and
753+ # skip the emit when it is no longer tracked, mirroring the
754+ # local_track_republished and remote track_unpublished handlers,
755+ # instead of raising a KeyError that _listen_task logs as an error.
749756 sid = event .local_track_unpublished .publication_sid
750- lpublication = self .local_participant .track_publications [sid ]
751- self .emit ("local_track_unpublished" , lpublication )
757+ unpublished = self .local_participant ._track_publications .get (sid )
758+ if unpublished is not None :
759+ del self .local_participant ._track_publications [sid ]
760+ self .emit ("local_track_unpublished" , unpublished )
761+ else :
762+ logging .debug ("local_track_unpublished for untracked publication sid %s" , sid )
752763 elif which == "local_track_republished" :
753764 # The SDK auto-republished a local track during a full
754765 # reconnect: the underlying Track (and its bound source) is
0 commit comments