@@ -759,15 +759,21 @@ def _on_room_event(self, event: proto_room.RoomEvent) -> None:
759759 unpublished = self .local_participant ._track_publications .get (sid )
760760 if unpublished is not None :
761761 del self .local_participant ._track_publications [sid ]
762- if unpublished .track is not None :
763- unpublished . track . _set_room ( None )
764- # Mirror track_unsubscribed: drop the publication's track
765- # reference. This also makes unpublish_track's own
766- # _set_room(None) a no-op when it loses the race (its
767- # `publication._track is not None` guard short-circuits),
768- # avoiding a redundant clear .
769- unpublished . _track = None
762+ track = unpublished .track
763+ if track is not None :
764+ track . _set_room ( None )
765+ # Emit while `publication.track` is still set, preserving the
766+ # pre-existing payload for callbacks. This handler is synchronous
767+ # and emit() invokes listeners synchronously, so nulling the track
768+ # right after still completes before any other coroutine (e.g .
769+ # unpublish_track) can interleave.
770770 self .emit ("local_track_unpublished" , unpublished )
771+ # Mirror track_unsubscribed: drop the publication's track
772+ # reference. This also makes unpublish_track's own _set_room(None)
773+ # a no-op when it loses the race (its `publication._track is not
774+ # None` guard short-circuits), avoiding a redundant clear.
775+ if track is not None :
776+ unpublished ._track = None
771777 else :
772778 logging .debug ("local_track_unpublished for untracked publication sid %s" , sid )
773779 elif which == "local_track_republished" :
0 commit comments