Environment
- livekit-agents==1.4.1 (also reproduced on 1.5.6)
- livekit==1.0.25 (Python wrapper around the Rust livekit-ffi)
- livekit-api==1.1.0
- Python 3.14
- macOS (local dev) and LiveKit Cloud (production)
- RUST_LOG=livekit=debug,livekit_api=debug
---
Repro
1. Start an AgentSession worker, have a remote participant join the room and exchange audio normally.
2. Cause a brief network blip on the agent host — Wi‑Fi network switch on macOS works every time; production
hits it on natural network blips.
3. Observe: WS keepalive ping times out, SDK enters Resume strategy, then stalls.
Two observed end-states:
- Variant A (stuck-in-reconnecting): connection_state_changed → 2 (Reconnecting), never returns to 1. No
further SDK activity for ~60s.
- Variant B (ghost-room): connection_state_changed → 1 (Connected) but remote_participants is the stale
pre‑blip snapshot. No audio frames flow and participant_disconnected never fires. Same SDK-side stall,
different surface symptom. (Related: livekit/agents#1581.)
---
Smoking-gun logs
The wedge (Variant A, the 2s window before we killed the process):
182443.187 WARN livekit::rtc_engine - received session close: "signal client closed: \"ping timeout\""
UnknownReason Resume
182443.188 ERROR livekit::rtc_engine - resuming connection... attempt: 0
<silence — no ICE candidates, no further signaling, no errors, no attempt: 1>
Earlier run where we let the SDK self-recover (same trigger, same attempt: 0 entry, no kill switch):
151555.557 ERROR livekit::rtc_engine - resuming connection... attempt: 0
<silence for ~65s>
151700.319 ERROR livekit::rtc_engine - resuming connection failed: signal failure: ws failure: Connection
closed normally
151700.319 ERROR livekit::rtc_engine - restarting connection... attempt: 1
→ Resume does eventually escalate, but only when the next ping cycle independently times out ~65s later.
That's the user-facing outage.
close event never fires during the wedge. @session.on("close") handler does not run. await session.aclose()
and await room.disconnect() both never return — confirmed with asyncio.wait_for(..., timeout=2.0) always
tripping the timeout in this state.
---
Why this looks like a bug, not expected behavior
(LiveKit support confirmed this off-thread, paraphrasing their reply:)
▎ A ping timeout followed by resuming connection... attempt: 0 with no ICE restart and no escalation for ~60s
▎ is not consistent with the documented reconnect flow. Given that close never fires, this is a stalled resume
▎ path rather than expected behavior.