acoustic-pharmacist-34178
03/10/2026, 5:14 PMroom = get_job_context().room
async with LiveKitAPI() as lkapi:
await lkapi.room.delete_room(DeleteRoomRequest(
room=room
))
13:09:10.576 ERROR livekit.agents exception occurred while executing tool
Traceback (most recent call last):
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/agents/voice/generation.py", line 623, in _traceable_fnc_tool
val = await function_callable()
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/var/www/livekit-agent/tools/hangup.py", line 32, in sip_hangup
await lkapi.room.delete_room(DeleteRoomRequest(
~~~~~~~~~~~~~~~~~^
room=room
^^^^^^^^^
))
^
TypeError: bad argument type for built-in operationtall-airline-16756
03/10/2026, 5:14 PMtall-airline-16756
03/10/2026, 5:15 PMdelete_room(DeleteRoomRequest(...)) call.
What to change
• Use the room’s name string instead of the Room object.
Quick fix (recommended):
room = get_job_context().room
async with LiveKitAPI() as lkapi:
await lkapi.room.delete_room(DeleteRoomRequest(
room=room.name # pass the name (string), not the Room object
))
Two convenient alternatives
• Let the JobContext handle it for you:
ctx = get_job_context()
await ctx.delete_room() # uses the room name internally
• Call the API directly but still pass the name:
from livekit import api
ctx = get_job_context()
async with api.LiveKitAPI() as lkapi:
await lkapi.room.delete_room(api.DeleteRoomRequest(room=ctx.room.name))
If you want, share the output of:
• type(get_job_context().room) and get_job_context().room (or get_job_context().room.name) — I can confirm what you’re getting and make sure the change will work in your environment.acoustic-pharmacist-34178
03/10/2026, 5:19 PM13:18:48.003 ERROR livekit.agents error while shutting down the job
Traceback (most recent call last):
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/agents/ipc/job_proc_lazy_main.py", line 379, in _run_job_task
await asyncio.gather(*shutdown_tasks)
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/agents/job.py", line 452, in wrapper
await callback() # type: ignore
^^^^^^^^^^^^^^^^
TypeError: object NoneType can't be used in 'await' expressiontall-airline-16756
03/10/2026, 5:20 PMawait asyncio.gather(*shutdown_tasks).
• One of the items in that list is None (or otherwise not awaitable), so Python raises: "object NoneType can't be used in 'await' expression".
Likely causes
• You called ctx.add_shutdown_callback(...) with a variable that is None.
• You added a non-async function (or a function that returns None instead of a coroutine) as a shutdown callback.
• You conditionally built a callback and accidentally passed None when the condition was false.
How to fix it
• Only add actual coroutine callables to shutdown callbacks. Ensure the value you pass to ctx.add_shutdown_callback is an async function (callable that returns a coroutine when called) or a coroutine function reference.
Quick checks to run
1. Search your code for every ctx.add_shutdown_callback(...).
2. For each callback, verify:
◦ It is not None.
◦ It is an async function (defined with async def) or a callable that returns a coroutine.
◦ You are passing the function object (not the result of calling it). For example, pass on_shutdown not on_shutdown().
Safe patterns
1) Add only if present:
if on_shutdown is not None:
ctx.add_shutdown_callback(on_shutdown)
2) Ensure the callback is async:
async def on_shutdown():
# cleanup
await some_cleanup()
ctx.add_shutdown_callback(on_shutdown) # pass function, not call
3) Wrap optional cleanup into a guaranteed coroutine:
def maybe_create_shutdown_task():
if need_cleanup:
async def _cleanup():
await do_cleanup()
return _cleanup
return None
shutdown_cb = maybe_create_shutdown_task()
if shutdown_cb:
ctx.add_shutdown_callback(shutdown_cb)
4) Defensive runtime check (useful for debugging):
cb = possible_cb
if cb is None:
logger.warning("Skipping None shutdown callback")
elif not asyncio.iscoroutinefunction(cb):
logger.error("Shutdown callback is not async: %s", cb)
else:
ctx.add_shutdown_callback(cb)
If you'd like, share the lines where you call ctx.add_shutdown_callback(...) (or the function that builds those callbacks). I can quickly scan them and point out exactly which callback is None or non-async.
Sources: Job lifecycle | LiveKit Documentationacoustic-pharmacist-34178
03/10/2026, 6:26 PM14:23:43.025 WARNI… livekit.agents failed to stream event to clients
Traceback (most recent call last):
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/agents/voice/client_events.py", line 509, in _send_client_event
writer = await self._room.local_participant.stream_text(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<2 lines>...
)
^
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/rtc/participant.py", line 581, in stream_text
await writer._send_header()
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/rtc/data_stream.py", line 189, in _send_header
raise ConnectionError(cb.send_stream_header.error)
ConnectionError: engine: connection error: engine is closed
{"pid": 90470, "job_id": "AJ_VaiM7nGXqpBT", "room_id": "RM_ocjbtWCECAMC"}
Traceback (most recent call last):
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/agents/voice/client_events.py", line 509, in _send_client_event
writer = await self._room.local_participant.stream_text(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<2 lines>...
)
^
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/rtc/participant.py", line 581, in stream_text
await writer._send_header()
File "/var/www/livekit-agent/lk/lib/python3.13/site-packages/livekit/rtc/data_stream.py", line 189, in _send_header
raise ConnectionError(cb.send_stream_header.error)
ConnectionError: engine: connection error: engine is closedtall-airline-16756
03/10/2026, 6:27 PMlocal_participant.stream_text(...) while the room/engine is already closed.
• That produces ConnectionError: engine: connection error: engine is closed.
• This is a race during shutdown: event emission still runs while the connection is being torn down.
What you can do (in order of recommendation)
1) Ensure client-events are stopped before closing the connection
• In your shutdown callback, stop any producers of events first, then close the client events handler, and only after that delete the room or close the session/engine.
Example pattern:
async def cleanup():
# 1. stop anything that generates events (interrupt session, stop tasks)
if hasattr(session, "interrupt"):
session.interrupt()
# 2. close client events handler (prevents further streaming attempts)
if hasattr(session, "_client_events_handler") and session._client_events_handler:
await session._client_events_handler.aclose()
# 3. delete room / close API connections
await api.LiveKitAPI().room.delete_room(api.DeleteRoomRequest(room=ctx.room.name))
2) Make event streaming resilient to a closed engine
• Before attempting to stream, check that the room/engine is still connected. If not, skip the send.
Example:
if not getattr(self._room, "is_connected", lambda: False)():
logger.debug("Room disconnected, skipping event stream")
return
try:
await self._send_client_event(event)
except ConnectionError as e:
if "engine is closed" in str(e):
logger.debug("Engine closed while sending event, ignoring")
else:
raise
3) Ensure callbacks you register are proper coroutines (to avoid other shutdown errors)
• When you add shutdown callbacks with ctx.add_shutdown_callback(...) ensure you pass an async function (not None and not the result of calling it). Example:
ctx.add_shutdown_callback(cleanup) # pass function object, not cleanup()
4) Optional: suppress noisy logs during shutdown (if the error is harmless for you)
• If this happens only during shutdown and you can’t easily reorder cleanup, temporarily lower the log level for that module:
import logging
logging.getLogger("livekit.agents.voice.client_events").setLevel(logging.ERROR)
Use this only if you’re confident the condition is harmless in your flow.
What I can check for you
• If you share the shutdown callback(s) and where you register or create the client events handler (the code around ctx.add_shutdown_callback(...) and session start/stop), I can point to the exact line that should be reordered or guarded.
• If you prefer, paste the code that emits events so I can suggest the precise guard to add.
If you want, start by pasting your shutdown callback and where you create/attach the client events handler — I’ll mark exactly what to change.