Skip to content

fix(inference): guard agent sid header against unconnected room#5947

Merged
tinalenguyen merged 1 commit into
mainfrom
ac/dazzling-sammet-a6056b
Jun 3, 2026
Merged

fix(inference): guard agent sid header against unconnected room#5947
tinalenguyen merged 1 commit into
mainfrom
ac/dazzling-sammet-a6056b

Conversation

@adrian-cowham
Copy link
Copy Markdown
Contributor

Summary

Fixes a crash in get_inference_headers() introduced by #5937 and carried through #5943. STT/TTS/LLM inference connections crash with Exception: cannot access local participant before connecting when their websocket opens before ctx.connect() finishes.

Root cause

#5937 added ctx.agent.sid to the header builder. ctx.agent resolves to room.local_participant, which raises a bare Exception (not RuntimeError) while the room is unconnected. The surrounding handler only caught RuntimeError (the no-job-context case), so the exception escaped and killed the STT pump:

File ".../inference/_utils.py", line 59, in get_inference_headers
    if isinstance(agent_sid := ctx.agent.sid, str) and agent_sid:
File ".../job.py", line 425, in agent
    return self._room.local_participant
File ".../rtc/room.py", line 210, in local_participant
    raise Exception("cannot access local participant before connecting")

Before #5937 the function only read ctx.job.room.sid and ctx.job.id (both from job info, available pre-connect), so it never touched local_participant.

Fix

Guard the access with room.isconnected() — the codebase-standard readiness check (utils/participant.py, job.py):

if ctx.room.isconnected() and isinstance(agent_sid := ctx.agent.sid, str) and agent_sid:
    headers[HEADER_AGENT_ID] = agent_sid

rtc.Room._local_participant is assigned once on connect and never cleared (not on disconnect or reconnect), so local_participant raises only before the first connect — exactly when isconnected() is False. The guard is therefore reliable, not a leaky proxy. MockRoom already stubs isconnected() -> True, so console/IPC mode is unaffected.

The agent id is best-effort attribution (per #5937); room and job ids stay available pre-connect, so attribution falls back to room/job level rather than crashing.

Testing

New tests/test_inference_utils.py (5 explicit cases): unconnected-room (the regression), connected, non-str sid (#5943 guard), empty sid, and no-job-context. The regression test fails on the pre-fix code with the exact reported error and passes with the fix.

  • ruff format --check + ruff check: clean
  • mypy -p livekit.agents: no new errors in inference/
  • Inference suite: 65 passed

Note

For a websocket opened before ctx.connect(), the agent id stays omitted for that connection's lifetime (headers are fixed at open time). That's by design for a best-effort attribution header, not a defect.

🤖 Generated with Claude Code

get_inference_headers() accessed ctx.agent.sid, which resolves to
rtc.Room.local_participant and raises a bare Exception before the room
is connected. STT/TTS websockets that open before ctx.connect() crashed
with "cannot access local participant before connecting", since the
handler only caught RuntimeError.

Guard the access with room.isconnected() (the codebase-standard
readiness check used in utils/participant.py and job.py);
local_participant is set once on connect and never cleared, so the
access cannot raise once isconnected() is True. Room and job ids come
from job info and remain available pre-connect, so attribution falls
back to room/job level. Adds regression tests covering the unconnected,
connected, non-str sid, empty sid, and no-job-context paths.

Regression introduced by #5937, latent through #5943.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chenghao-mou chenghao-mou requested a review from a team June 3, 2026 00:43
Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

@tinalenguyen tinalenguyen merged commit 5f029b9 into main Jun 3, 2026
26 checks passed
@tinalenguyen tinalenguyen deleted the ac/dazzling-sammet-a6056b branch June 3, 2026 01:02
Bobronium added a commit that referenced this pull request Jun 3, 2026
New module from main (#5947); it covers get_inference_headers with fakes (no
network or credentials), so it's a hermetic unit test.

https://claude.ai/code/session_01XvWkVuQVX9kJf3gn9cRXC5
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants