This message was deleted.
# ask-ai
s
This message was deleted.
t
Thanks for reaching out, G.. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I hear you: you’re seeing many concurrent agent sessions even though you don’t have active conversations. I’ll help you cleanly close agent sessions and stop those orphaned sessions. Before I give concrete steps, a couple quick clarifying questions so I give the right instructions: • Is your agent started once per room, or is a separate agent started per participant? • Are you using plain WebRTC via the Expo client, or are you using SIP / Phone Gateway integrations? • Do you currently call any shutdown or room deletion logic in your Node.js agent when the client leaves? If you want to try fixes right away, follow these practical steps (Node.js agent + Expo client): 1) Gracefully shut down the agent session • When a conversation ends, call the agent shutdown API so the agent finishes any in-progress speech and cleans up. In your agent entry, call: ◦
ctx.shutdown({ drain: true })
to gracefully drain speech before closing, or ◦
await ctx.aclose()
for immediate close. 2) Delete the room when the session should end • Deleting the room removes all participants and prevents orphan agents from remaining. Use the RoomService API to delete: ◦ Instantiate RoomServiceClient with your LiveKit API URL, key, and secret, then call
deleteRoom(roomName)
when the last user leaves. 3) Ensure the Expo client disconnects cleanly • In your Expo app, call
await room.disconnect()
whenever the user leaves the call or the component unmounts. If the client crashes or doesn’t disconnect, LiveKit will keep state until the disconnect timeout. 4) Auto-clean when participants leave • In your agent code listen for participant/room events and clean up automatically. Example logic: ◦ On
participant_disconnected
, if
remoteParticipants.size === 0
, call room deletion or
ctx.aclose()
to remove the agent. ◦ Also handle
room.disconnected
events to trigger shutdown logic. 5) Add shutdown hooks • Register a
ctx.addShutdownCallback()
to run any finalization (saving state, closing resources) after
shutdown()
runs. Why these steps fix lingering sessions • If clients don’t disconnect or you don’t delete the room, agents can remain in rooms until timeouts occur. Explicitly shutting down the agent and deleting the room prevents orphaned sessions and frees resources. If you can share: • Whether agent-per-room or agent-per-participant is used, • A short snippet of how you currently start/stop the agent (Node.js), • Whether the Expo client calls
room.disconnect()
on leave, I’ll provide exact code snippets tailored to your setup and a suggested minimal lifecycle flow to prevent the concurrent session buildup. Sources: Job lifecycle | LiveKit Documentation | Why is there extra audio at the end of my recordings of an agent session? |... | Understanding the STATE_MISMATCH Disconnect Reason | LiveKit
Thanks — I appreciate the extra detail. You’re seeing the session Close event and your shutdown callback run, but concurrent sessions still persist. Let’s troubleshoot this step-by-step so we can find what’s not getting cleaned up. First, a few targeted questions that will quickly narrow down the likely causes: • When you see the Close event and shutdown callback run, does the agent process (or the Node process) actually exit, or does it continue running? • Do you delete the LiveKit room after shutdown, or rely on participant disconnect/timeouts? If you don’t delete the room, which RoomService call (if any) do you currently run? • Is your agent started once per room or spawned per participant? (Agent-per-participant can create many sessions if not torn down per participant.) • Are there any long-running tasks (eg. egress, recordings, external API calls) started by the agent that might keep the process or room alive? • Do you see any errors thrown during shutdown in logs (including from RoomService delete or network calls)? • On the Expo side, do you always call
room.disconnect()
when leaving, and do you observe the client side disconnect event in LiveKit dashboard/metrics? Things to check and try right now 1) Confirm agent shutdown completes (not only callback invoked) • After
ctx.shutdown()
/
ctx.aclose()
, verify code does not keep references to the session or schedule asynchronous tasks that keep Node’s event loop alive. Example culprits: unawaited promises, open timers, pending HTTP requests, or active egress/recording streams. 2) Explicitly delete the room when last participant leaves • Even if shutdown runs, if the room remains the agent can be re-created or counted as active. On the agent or server side, call RoomServiceClient.deleteRoom(roomName) when remoteParticipants.size === 0. 3) Ensure egress/recording is stopped before shutdown • If you start an egress or recording, stop/cancel that explicitly before/inside shutdown. Egress can keep resources allocated and sessions counted. 4) Add a final verification that the participant/agent disconnected from LiveKit • After shutdown, poll or inspect the room state (via RoomService or LiveKit dashboard) to confirm the agent is no longer listed as a participant. Log that result for diagnosis. 5) Increase shutdown visibility in logs • Add logs for: ◦ Start of shutdown ◦ Each awaited cleanup step (stop TTS, stop STT, stop egress, delete room) ◦ Final confirmation that the room no longer contains the agent Example minimal sequence to ensure full cleanup 1. stop/close any TTS/STT streams and cancel long-running tasks 2. stop any egress/recording 3. call
ctx.shutdown({ drain: true })
or
await ctx.aclose()
4. call
roomServiceClient.deleteRoom(roomName)
if you want immediate cleanup 5. verify via RoomService that the room is gone or agent participant removed If you’re comfortable sharing a short snippet showing: • How you start the session (where
AgentSession
is created), • The shutdown code (the exact
ctx.shutdown
/
ctx.aclose
usage and what you await), • Any egress/recording logic, I’ll review it and point out the exact place something may still be keeping sessions alive and provide a concrete code fix.
Thanks — I hear you: you’re seeing the Close event and shutdown callback run, but four agent sessions still appear 30 minutes later. That’s frustrating. Let’s focus on the highest-probability causes and a short checklist you can run now to pinpoint and fix the leak. Quick checklist to run now 1. Confirm the room was deleted • After your shutdown callback runs, check whether you call RoomServiceClient.deleteRoom(roomName). If you do not delete the room, the agent participant can remain listed even after shutdown. • If you already call deleteRoom, log the delete response or any error to confirm it succeeded. 1. Confirm the agent participant is removed from the room • Immediately after shutdown (and after deleteRoom if used), query the room or use the LiveKit dashboard to confirm the agent participant is no longer present. Add a log line showing the participant list size. 1. Verify Node’s event loop can exit • Make sure you are not leaving any active timers, un-awaited promises, or open streams (TTS/STT/egress) that keep the process alive. Add a log at the very end of your shutdown sequence (e.g., “shutdown complete — process should exit”). 1. Ensure any egress/recording is stopped • If you start egress or recording, stop it explicitly before or during shutdown. Egress sessions can keep resources allocated and appear as active sessions. 1. Confirm the client disconnects • On the Expo client, ensure you always call
room.disconnect()
when leaving or unmounting, and log the disconnect call. If the client remains connected (even in a zombie state), LiveKit won’t remove the room automatically. Immediate small tests to run • Manually call deleteRoom for one of the rooms showing lingering agents and see if the agent session count drops immediately. This proves whether room deletion is the missing step. • Add a short script that: 1) calls
ctx.shutdown({drain:true})
or
await ctx.aclose()
2) stops egress/recording 3) calls RoomServiceClient.deleteRoom(roomName) 4) queries the room participants and logs the result If this clears the session, the issue is missing room deletion or lingering egress. What I suspect (and why) • You are seeing Close and shutdown callbacks, but the room itself is not being deleted (or some egress/stream is still active). Shutting down an AgentSession alone does not always remove the LiveKit room or external egress, so sessions can remain counted until you delete the room or stop those resources. If you want me to inspect further, please share these minimal snippets: • The code that starts the AgentSession (how you create/start
session
). • The shutdown sequence (exact usage of
ctx.shutdown()
/
ctx.aclose()
and anything awaited afterwards). • Any egress/recording code you run. • The Expo disconnect logic (where
room.disconnect()
is called). With those I’ll: • Point out the exact missing await or cleanup call. • Give a corrected shutdown sequence (small code patch) that ensures the agent process and room are fully removed. If you’d prefer, I can provide the minimal: 1) safe shutdown sequence, and 2) a troubleshooting script you can run immediately to force-delete rooms and verify sessions drop. Which would you like next? Sources: Job lifecycle | LiveKit Documentation | Agent session | LiveKit Documentation | LiveKit coming to React Native! | Why is there extra audio at the end of my recordings of an agent session? |...