```2026-04-03T13:49:14.870491124Z INFO:livekit:liv...
# ask-ai
n
Copy code
2026-04-03T13:49:14.870491124Z INFO:livekit:livekit::room:1506:livekit::room - disconnected from room with reason: RoomClosed
2026-04-03T13:49:14.871844042Z ERROR:livekit.agents:Error in _start_task
2026-04-03T13:49:14.871872948Z Traceback (most recent call last):
2026-04-03T13:49:14.871879421Z   File "/usr/local/lib/python3.13/site-packages/livekit/agents/utils/log.py", line 17, in async_fn_logs
2026-04-03T13:49:14.871884102Z     return await fn(*args, **kwargs)
2026-04-03T13:49:14.871888366Z            ^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-03T13:49:14.871893097Z   File "/usr/local/lib/python3.13/site-packages/livekit/agents/voice/avatar/_datastream_io.py", line 96, in _start_task
2026-04-03T13:49:14.871903502Z     await utils.wait_for_participant(room=self._room, identity=self._destination_identity)
2026-04-03T13:49:14.871908196Z   File "/usr/local/lib/python3.13/site-packages/livekit/agents/utils/participant.py", line 113, in wait_for_participant
2026-04-03T13:49:14.871910965Z     return await fut
2026-04-03T13:49:14.871913489Z            ^^^^^^^^^
2026-04-03T13:49:14.871916083Z RuntimeError: room disconnected while waiting for participant
2026-04-03T13:49:14.871918672Z Traceback (most recent call last):
2026-04-03T13:49:14.871921225Z   File "/usr/local/lib/python3.13/site-packages/livekit/agents/utils/log.py", line 17, in async_fn_logs
2026-04-03T13:49:14.871923758Z     return await fn(*args, **kwargs)
2026-04-03T13:49:14.871926270Z            ^^^^^^^^^^^^^^^^^^^^^^^^^
2026-04-03T13:49:14.871928821Z   File "/usr/local/lib/python3.13/site-packages/livekit/agents/voice/avatar/_datastream_io.py", line 96, in _start_task
2026-04-03T13:49:14.871931299Z     await utils.wait_for_participant(room=self._room, identity=self._destination_identity)
2026-04-03T13:49:14.871933781Z   File "/usr/local/lib/python3.13/site-packages/livekit/agents/utils/participant.py", line 113, in wait_for_participant
2026-04-03T13:49:14.871936243Z     return await fut
2026-04-03T13:49:14.871938810Z            ^^^^^^^^^
2026-04-03T13:49:14.871941408Z RuntimeError: room disconnected while waiting for participant
2026-04-03T13:49:14.873166242Z INFO:livekit.agents:process exiting
✅ 1
t
Thanks for reaching out, Darshak. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I see you're hitting a RuntimeError: "room disconnected while waiting for participant" from an agents voice/avatar task, and the room disconnect reason in the logs is
RoomClosed
. I'll walk through what this means, likely causes, and practical next steps to prevent or handle it. What the error means • Your agent called
wait_for_participant(...)
while connected to a LiveKit room and was blocking until a specific participant joined. • The room connection ended (log shows reason:
RoomClosed
) before that participant arrived, so
wait_for_participant
raised
RuntimeError: room disconnected while waiting for participant
. Likely causes • The room was deleted or closed (for example via a DeleteRoom API call) before the participant joined. • The room auto-closed because no participants connected and the room’s idle/departure timeout expired. • The client you expected to join never connected or disconnected prematurely. • The agent lost its connection (job context disconnected) while waiting; the agent framework does not automatically reconnect after a RoomClosed event. What you can do right now 1. Confirm the room lifecycle events ◦ Check whether something (an API call or admin action) deleted or explicitly closed the room around the same timestamp. ◦ Check client-side logs for the participant you expected to join to see whether they attempted to connect and whether their connection succeeded or failed. 1. Add graceful error handling around wait_for_participant ◦ Catch the RuntimeError and: ▪︎ Log the event with room/identity/job IDs for correlation. ▪︎ Retry waiting a small number of times with a short backoff if the room is reconnectable in your flow. ▪︎ Or abort the agent task cleanly and mark the job as failed/safe-stop so it doesn’t hang. 1. Consider these mitigations depending on your workflow ◦ Ensure the participant joins promptly after the agent starts waiting — e.g., coordinate signaling so agent starts only after an initial client join attempt. ◦ If rooms auto-close due to idle time, increase the room idle/departure timeout (if applicable) so the room remains open long enough for the participant to join. ◦ If external services might delete rooms, add checks for room existence before starting wait logic and handle deletions explicitly. Suggested code-level approach (conceptual) 1. Wrap
wait_for_participant
in try/except: ◦ On success: continue normal flow. ◦ On
RuntimeError
with room closed: either retry connect/ wait a configurable number of times OR exit gracefully and surface a clear error. 1. Optional: poll room state before waiting: ◦ If the room is already closed/marked for deletion, skip waiting and handle the condition immediately. Information that would help me give a targeted fix • Are your agents started automatically when a room is created, or do you start them after a participant connects? • Do you have any logic that programmatically deletes rooms, or are you relying on an idle timeout? • The job/room IDs and timestamps around 2026-04-03T134914 (if you can share) to correlate server-side events and confirm whether a DeleteRoom call or idle timeout occurred. • A short snippet of how you call
wait_for_participant
(or agent start/connect flow) so I can suggest exact try/except and retry logic. If you share one or two of those details I’ll give a concise example of robust error handling and retry logic tailored to your code. Sources: Agent session | LiveKit Documentation | Understanding the STATE_MISMATCH Disconnect Reason | LiveKit | Job lifecycle | LiveKit Documentation | Handling asynchronous metadata updates in LiveKit | LiveKit, with interactive elements
n
if possible to add keyboard typing/ bg noise while executing only a tool called ?
t
Yes — you can play keyboard typing or background noise while a specific tool executes. What you can use • Use the BackgroundAudioPlayer with
thinking_sound
(it plays during the agent’s "thinking" state, which includes tool calls). • Built-in clips:
BuiltinAudioClip.KEYBOARD_TYPING
,
KEYBOARD_TYPING2
, and
OFFICE_AMBIENCE
. • You can also play local files or stream raw audio frames. Two ways to apply it • Automatic during tool calls: configure
BackgroundAudioPlayer(thinking_sound=[...])
and start it for the agent/session; the sound will play during tool execution. • Manual for a specific tool: call
background_audio.play("/path/to/typing.mp3")
(and stop when done) from the tool’s logic for precise control. Example (conceptual Python)
Copy code
background_audio = BackgroundAudioPlayer(
    thinking_sound=[
        AudioConfig(BuiltinAudioClip.KEYBOARD_TYPING, volume=0.8),
    ],
)
await background_audio.start(room=ctx.room, agent_session=session)

# Or play on-demand inside a tool
await background_audio.play("/path/to/typing.mp3")
# ...run tool...
await background_audio.stop()
Questions to tailor this for you • Do you want the sound only for one specific tool or for all tool calls? • Are you using built-in clips or custom audio files? • Do you need randomization/probabilities or varying volume? Tell me those and I’ll provide the exact code snippet and placement for your agent/tool. Sources: Background audio | LiveKit Documentation | Bring your LangChain Agents to LiveKit | LiveKit, with interactive elements
n
only for a tool called ? not while LLM call happening
t
Got it — you want audio only during a specific tool, not during LLM calls. That’s possible by controlling the BackgroundAudioPlayer manually inside the tool logic. Recommended approach 1. Create a shared BackgroundAudioPlayer (or instantiate in the tool). 2. In the tool’s start logic, call
await background_audio.play(...)
with a built-in clip (
BuiltinAudioClip.KEYBOARD_TYPING
) or a file path. 3. Run the tool work. 4. Immediately call
await background_audio.stop()
when the tool finishes (or on error). Concise example (conceptual Python)
Copy code
# created once and passed into tool or accessible from context
background_audio = BackgroundAudioPlayer()

async def my_tool(ctx, ...):
    await background_audio.play(BuiltinAudioClip.KEYBOARD_TYPING)  # start typing sound
    try:
        # perform the tool's work (avoid calling LLM here if you don't want sound during LLM)
        result = await do_tool_work(...)
    finally:
        await background_audio.stop()  # ensure it always stops
    return result
Notes • Ensure the LLM call is outside the play/stop window (or pause/stop audio before LLM). • You can use built-in clips, local files, or raw audio frames. • Use try/finally to guarantee stopping on errors. Do you want a full copy-paste-ready snippet for your agent framework (Python) including imports and initialization? , with interactive elements
n
tool_execution_started_cb how to register this callback ?
t
I can help — which SDK/language and agent version are you using (Python, JS, or other)? Also: do you want the callback registered globally for all tools or only for a specific tool instance? , with interactive elements
n
class Toolset: @dataclass class ToolCalledEvent: ctx: RunContext arguments: dict[str, Any] @dataclass class ToolCompletedEvent: ctx: RunContext output: Any | Exception | None if possible to add keyboard typing/ bg noise while only a tool is being called ? or can i get any event when tool is called ?
t
Thanks — I’ll help you trigger typing/bg audio only when a specific tool runs and how to detect that tool call. Short answer • Use the agent’s tool execution path to start/stop BackgroundAudioPlayer inside the tool’s execute function (best control). • If you need a callback, listen for the agent/session events that indicate a tool call (conversation/function_call or agent state changing to "thinking") and start/stop audio there. Python-focused guidance (since your Toolset looks Pythonic) 1) Inside the tool implementation (recommended) • In the tool’s
execute
(or equivalent) method, start the audio, run the tool work, then stop audio in a try/finally so LLM calls aren’t covered. Example (concise)
Copy code
background_audio = BackgroundAudioPlayer()

async def my_tool_execute(ctx, args):
    await background_audio.play(BuiltinAudioClip.KEYBOARD_TYPING)
    try:
        result = await do_tool_work(args)   # no LLM calls inside here if you don't want audio during LLM
    finally:
        await background_audio.stop()
    return result
2) If you prefer a callback / central hook • There isn’t a documented built-in
tool_execution_started_cb
event name guaranteed across versions, but you can detect tool calls by: ◦ Listening to conversation events such as
function_call
/
function_call_output
emitted by the AgentSession, or ◦ Observing agent state transitions (-> "thinking") which correspond to tool execution. Conceptual example listening on session events:
Copy code
def on_conversation_item(event):
    if event.type == "function_call" and event.function_name == "my_tool":
        await background_audio.play(BuiltinAudioClip.KEYBOARD_TYPING)

    if event.type == "function_call_output" and event.function_name == "my_tool":
        await background_audio.stop()
What I recommend • Preferred: implement start/stop inside the tool’s execute so it’s precise and guaranteed to only wrap that tool. • Alternate: subscribe to session conversation events if you need a centralized callback for multiple tools. Tell me which SDK/version you’re using (Python agent version) and whether you want per-tool code or a global listener, and I’ll provide a copy-paste-ready snippet tailored to your setup. Sources: Background audio | LiveKit Documentation | Agent session | LiveKit Documentation | Testing and evaluation | LiveKit Documentation | Turns overview | LiveKit Documentation, with interactive elements
n
• Alternate: subscribe to session conversation events if you need a centralized callback for multiple tools. yes this is what exactly needed but currently not getting any event of function_call in session
t
Thanks — I hear you: you want a centralized callback that fires when a tool is invoked (so you can start/stop typing/bg audio), but you’re not receiving
function_call
events in the session. Let’s troubleshoot and get it working. Quick checklist (please confirm each): • Which SDK and
livekit-agents
version are you using (Python or Node.js and the exact package version)? • Are your event handlers registered before you start the session (handlers must be attached prior to
session.start()
/
AgentSession
run)? • Are your tools defined as function tools (
@function_tool
in Python or
llm.tool()
in Node.js)? Only those generate function call items. • Do you see any conversation events at all (e.g.,
conversation_item_added
for regular messages)? If not, the session may not be started or events not wired correctly. How to subscribe (Python) • Register for conversation items (tool calls appear as items with type
function_call
):
Copy code
@session.on("conversation_item_added")
async def on_conversation_item_added(event):
    item = event.item
    if getattr(item, "type", None) == "function_call" and item.name == "my_tool":
        await background_audio.play(BuiltinAudioClip.KEYBOARD_TYPING)
    if getattr(item, "type", None) == "function_call_output" and item.name == "my_tool":
        await background_audio.stop()
Notes: • Use
"conversation_item_added"
as the event name (not
"function_call"
directly). • Ensure this decorator/handler is registered before
session.start()
. Alternate detection method • Listen for agent state transitions (-> "thinking") as a fallback; tool execution maps to the thinking state:
Copy code
@session.on("agent_state_changed")
async def on_state_changed(ev):
    if ev.new_state == "thinking":
        # start audio (but this is coarse-grained)
    elif ev.new_state == "speaking":
        # stop audio
If you still get no
function_call
items, provide: • SDK and package version, • A minimal snippet showing how you register handlers and start the session, • One example tool definition (so I can confirm tool registration syntax). Tell me those and I’ll give a copy-paste-ready listener + BackgroundAudioPlayer integration for your exact setup. Sources: Agent session | LiveKit Documentation | Testing and evaluation | LiveKit Documentation | Function tool definition | LiveKit Documentation, with interactive elements