What's the recommended way to wait for user partic...
# ask-ai
b
What's the recommended way to wait for user participant to join the room? Upon room creation, our ops listener (egress) automatically joins the room. This seems to occasionally cause a race condition where
ctx.wait_for_participant()
doesn't wait for the actual user (standard or sip skin) to join.
t
Thanks for reaching out, Masayuki. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — you're asking how to reliably wait for a real user participant to join a room when an automated ops/egress listener may join first and cause
ctx.wait_for_participant()
to return too early. Short answer: keep calling
ctx.wait_for_participant()
but filter out automated participant kinds (egress/agent) or check participant identity/metadata until you see a
STANDARD
or
SIP
user you expect. Example pattern (conceptual Python):
Copy code
participant = await ctx.wait_for_participant()
while participant.kind in (rtc.ParticipantKind.PARTICIPANT_KIND_EGRESS,
                           rtc.ParticipantKind.PARTICIPANT_KIND_AGENT):
    participant = await ctx.wait_for_participant()

# Now participant is a user (STANDARD or SIP) — or add further checks for identity/metadata
Notes and options: • If you need to match a specific user, also verify
participant.identity
or metadata and continue waiting until it matches. • For SIP participants, treat
SIP
as a valid user kind (not egress). • If your ops listener can be distinguished by identity or metadata, checking that is more robust than relying only on kind. Would you like a version of the snippet in a different language or guidance on checking identity/metadata for your specific setup? Sources: Job lifecycle | LiveKit docs | Using Noise Cancellation for SIP and WebRTC Participants
b
Doesn't that
while
cause an infinite loop? Can I use
kind=
argument?
t
You're right to worry about an infinite loop. The
while
loop can loop forever if no matching participant ever joins. Use the
kind=
argument on
ctx.wait_for_participant()
instead — it waits asynchronously for a matching participant (and resolves immediately if one is already present) and avoids spinning:
Copy code
# Wait only for a STANDARD participant
participant = await ctx.wait_for_participant(kind=rtc.ParticipantKind.STANDARD)

# Or wait for either STANDARD or SIP (example pattern)
participant = await ctx.wait_for_participant(kind=rtc.ParticipantKind.STANDARD)
if not participant:
    participant = await ctx.wait_for_participant(kind=rtc.ParticipantKind.SIP)
Recommendations: • Prefer
kind=
to filter automated participants like `EGRESS`/`AGENT`. • If you need a specific identity or metadata, after
wait_for_participant(kind=...)
verify
participant.identity
or metadata and call
wait_for_participant()
again if it doesn't match. • Add a timeout or cancellation around the await if there’s any chance no matching participant will ever join, to avoid waiting indefinitely. Would you like a short example that waits for either
STANDARD
or
SIP
with a timeout? Sources: Job lifecycle | LiveKit docs | Rooms, participants, and tracks | LiveKit docs | Managing participants | LiveKit docs