Skip to content

LiveKit Python SDK - unhandled InvalidStateError in _on_room_event silently drops local_track_subscribed after a full reconnect #770

Description

@ziv-nagish

Hi folks, We hit a small but reproducible bug in livekit-rtc that we've had to filter out of our alerting. Still present on main today.

SDK: livekit (rtc) 1.1.8 via livekit-agents 1.6.0 · CPython 3.11.14

What happens
On a full connection restart (not a resume), the server re-sends local_track_subscribed for a LocalTrackPublication the SDK reuses. Its _first_subscription future is already resolved, so set_result(None) raises:

error running user callback for local_track_subscribed: room_handle: 5
local_track_subscribed {
track_sid: "TR_AuA9ZzcJUN2tu"
}
Traceback (most recent call last):
File "/app/.venv/lib/python3.11/site-packages/livekit/rtc/room.py", line 675, in _listen_task
self._on_room_event(event.room_event)
File "/app/.venv/lib/python3.11/site-packages/livekit/rtc/room.py", line 752, in _on_room_event
lpublication._first_subscription.set_result(None)
asyncio.exceptions.InvalidStateError: invalid state

On main: livekit-rtc/livekit/rtc/room.py L814-818. _first_subscription is a one-shot asyncio.Future created in LocalTrackPublication.init (track_publication.py L90) — no re-arm, and no .done() guard at its only call site.

Concrete example — room ******, all times UTC
• 06:57:28.653 — session close: "signal client closed: "stream closed"" Resume → resuming connection... attempt: 0
• 06:57:29.846 — Room reconnected — resume succeeded, no error
• 06:57:44.894 — session close: "server request to leave" ConnectionTimeout Resume
• 06:58:00.084 — resuming connection failed: wait_pc_connection timed out → restarting connection... attempt: 1
• 06:58:00.697 — Room reconnected
• 06:58:00.806 — ← InvalidStateError, 109 ms after the restart completed

Within this one room the earlier resume produced no error and only the restart path did, which lines up with the publication object being reused across a full re-subscribe.

Same pattern, second instance in the same dispatcher
room.py L867-869, track_subscription_failed branch - self.remote_participants[identity] is also unguarded, so a participant that already left raises KeyError:
Room RM_EeSEX2UcEueR, 2026-07-21 16:35:37.103 - KeyError: 'user
*******', track TR_AMegE5x4sZ2k72. That one lands 6 ms after your Rust-side could not find published track with sid for the same track (16:35:37.098), on a call where the user hung up 0.8 s after connecting.

Minor secondary ask
_listen_task reports these via a bare logging.exception(...), so they land on the root logger rather than livekit. That means we can't scope them with normal per-library log config - we ended up adding a root-logger filter matched on message text. A logging.getLogger(name) there would help.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions