I have the following setup for a voip agent: on_pa...
# ask-ai
b
I have the following setup for a voip agent: on_participant_disconnected that calls self.session.shutdown(drain=True) whenever one of the participants disconnect (Im using this hook to handle different states during transfer calls and so on) This triggers the on_exit hook in the agent which calls: await ctx.api.room.delete_room(delete=api.DeleteRoomRequest(room=ctx.room.name)) I then have a shutdown hook that publishes the transcript and some other stuff. I've been reviewing the logs, and sometimes something weird happens: -> on_participant_disconnected is called -> on_exit is called (which deletes the room) -> the shutdown hook is not called (i have a print at the beginning of the shutdown hook, so i know it's not hanging) -> 10 seconds after the on_exit is called i get "process did not exit in time", "killing process", "sending SIGUSR1 signal to process", "process exited with non-zero exit code -10" This makes my pipeline fail, as the transcript is never published. What may be wrong?
t
Thanks for reaching out, Alessandro. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I looked over your sequence and logs. You’re seeing the shutdown hook skipped because deleting the room inside
on_exit
causes the agent process to be disconnected/terminated before shutdown hooks run. What’s happening • Calling
delete_room
from
on_exit
forces an immediate room-wide disconnect. • That terminates the agent session out-of-band, so the framework hits the process timeout and kills the job (~10s), preventing registered shutdown callbacks from executing (so your transcript publish never runs). What to change (two safe options) • Preferred: Move transcript publish and room deletion into a shutdown callback so they run after session shutdown. a. Register a shutdown hook with
ctx.add_shutdown_callback()
that publishes the transcript first, then calls
ctx.api.room.delete_room(...)
. b. In
on_participant_disconnected
, call
self.session.shutdown(drain=True)
but do NOT call
delete_room
. • Alternative: Use automatic cleanup by enabling
delete_room_on_close=True
when starting the session (Python only). That deletes the room after the session fully closes and preserves shutdown hook execution. Quick example (shutdown hook approach)
Copy code
async def shutdown_hook():
    await publish_transcript(...)
    await ctx.api.room.delete_room(api.DeleteRoomRequest(room=ctx.room.name))

ctx.add_shutdown_callback(shutdown_hook)

# on participant disconnect:
await self.session.shutdown(drain=True)  # do not delete room here
If you'd like, share the exact hooks/code snippets for
on_exit
, the shutdown registration, and where you call
delete_room
and I’ll point out the minimal edits to apply. Sources: Job lifecycle | LiveKit docs | Agent session | LiveKit docs
b
when calling self.session.shutdown(drain=True) before it actually runs the shutdown callback it takes tens of seconds, leaving the room open and if the call is terminated by the agent, the user remains alone in the call
t
Thanks — I see you’re asking why
self.session.shutdown(drain=True)
can take tens of seconds before shutdown callbacks run, leaving the room open and the user alone. That’s frustrating; here’s how to fix it. Why it delays •
drain=True
waits for the agent to finish any queued audio (TTS or playing responses). If the agent is mid-response or has long queued speech, shutdown waits for that to finish, which can take many seconds. Shutdown callbacks don’t run until the drain completes. Options to make shutdown prompt and avoid leaving the user alone • Immediate shutdown: call
self.session.shutdown(drain=False)
(or
await session.aclose()
) to close the session right away and let shutdown callbacks run immediately. Use this if you don’t need to finish playing remaining audio. • Shorten drain time: keep agent responses short so draining finishes faster. • Always delete the room after session ends in a shutdown callback so the user isn’t left alone:
Copy code
async def shutdown_hook():
      await publish_transcript(...)
      await ctx.api.room.delete_room(api.DeleteRoomRequest(room=ctx.room.name))
  ctx.add_shutdown_callback(shutdown_hook)
• Or use
delete_room_on_close=True
when starting the session (Python only) so the room is removed after the session fully closes. Questions to help me give exact edits • Do you need the agent’s remaining audio to play out, or is immediate stop acceptable? • Are you using Python and can enable
delete_room_on_close
, or do you prefer explicit
delete_room
in a shutdown hook? Tell me which option you prefer and I’ll give the minimal code changes to implement it. Sources: Job lifecycle | LiveKit docs
b
Im currently trying: -> self.session.shutdown() on participant disconnect -> the session on_exit is called immediately. (im not doing anything in there, just logging) -> session is closed immediately from the logs -> shutdown callback is called after 30 seconds or more. I want the shutdown callback to be called immediately
t
Thanks — I hear you: you call
self.session.shutdown()
on participant disconnect,
on_exit
runs immediately, but your shutdown callback only fires 30+ seconds later and you want it to run right away. Here are concise, actionable options and one quick question. Why this happens •
shutdown()
starts a graceful shutdown that waits for pending media/speech/drain and internal cleanup; shutdown callbacks run only after that completes, which can take tens of seconds. Options to make the shutdown callback run immediately • Immediate async close (recommended if you don't need to finish playing audio):
Copy code
await self.session.aclose()
This forces the session to close synchronously so shutdown callbacks run right away. • Use shutdown without draining (may still have small delay):
Copy code
self.session.shutdown(drain=False)
This stops draining but can still incur connection-cleanup delay. • Put time-sensitive logic in on_exit (runs immediately on session exit):
Copy code
class MyAgent(Agent):
      async def on_exit(self):
          # urgent tasks here
• Combine approaches: run the urgent steps (e.g., delete room) directly on participant disconnect or in on_exit, and reserve longer work (publish transcript) for shutdown callbacks. Quick question • Do you need any remaining agent audio to finish playing for the user? If no, use
await self.session.aclose()
and I’ll show the minimal code change. If yes, we’ll need a different approach (e.g., move only urgent cleanup to on_exit). Sources: Job lifecycle | LiveKit docs | Server lifecycle | LiveKit docs | Agent speech and audio | LiveKit docs