white-room-9349
12/19/2025, 8:55 PMtall-airline-16756
12/19/2025, 8:55 PMtall-airline-16756
12/19/2025, 8:56 PMInvalidRequest("handle not found") when calling CaptureAudioFrame. That means the FFI handle (source_handle: 37) does not map to an active AudioSource when you attempted to capture a frame.
To help you fix it, please tell me:
• Are you creating and holding a reference to the AudioSource when you call capture_frame? (Share the short code path that creates the source and the code that calls capture.)
• Do you call `close()`/`aclose()` or end the session before the capture runs?
• Is capture being called from another thread/task or immediately after source creation (possible race)?
• Which SDK/language and LiveKit Agent version are you using?
Quick checks you can run now:
1. Ensure the AudioSource object stays in scope (keep a strong reference) until all captures finish.
2. Don’t call `close()`/`aclose()` on the source before the last capture_frame.
3. If capture runs asynchronously, add a short delay or await source initialization before the first capture to avoid a race.
4. Verify you aren’t reusing stale handle IDs across sessions — create a fresh AudioSource for each session.
Send the minimal code snippet that creates the source and the capture call and I’ll point out exactly where to change it.
Sources: Playing Audio | LiveKit docs | Camera & microphone | LiveKit docswhite-room-9349
12/19/2025, 8:59 PMtall-airline-16756
12/19/2025, 8:59 PMcapture_frame (or equivalent) with that source,
• any `close()`/`aclose()` or session cleanup logic,
• whether capture runs in another task/thread and any awaits around creation.
If you can’t share code, answer these quick diagnostics:
• Do you keep a strong reference to the AudioSource until after all captures finish?
• Do you call `close()`/`aclose()` before the last capture?
• Is capture possibly invoked before the source is fully initialized (race)?
• Are you reusing handles across sessions?
Share the snippet or answers and I’ll point out the exact fix.white-room-9349
12/19/2025, 9:08 PMtall-airline-16756
12/19/2025, 9:08 PMAudioSource (or LocalAudioTrack) and where you keep the reference.
• The code for the silent tool call (or the call site) and whether it runs in a separate async task/thread.
• Any calls to `close()`/`aclose()` or cleanup that run around the tool call.
• The LiveKit Agents package exact version (you listed ~1.2; confirm the installed version output from pip show livekit-agents).
If you can’t share code, answer these:
• Does the tool call run in a separate task or awaited inline?
• Do you create a new AudioSource per speak session or reuse one across sessions?
• Do you explicitly close the source when the tool runs or immediately after speaking?
With that I’ll point to the exact change (likely keeping the AudioSource alive across the tool call or avoiding closing it until all captures finish) and show the minimal code fix.white-room-9349
12/19/2025, 9:11 PMtall-airline-16756
12/19/2025, 9:12 PMAudioSource per speak session or keep a persistent instance? show the creation line and where you store the reference.)
2. Where do you call capture_frame (or equivalent)? Is that call in the same async task/flow as creation or in a separate task/thread?
3. Do you call `close()`/`aclose()` or otherwise release the source when speaking ends or when the tool runs? If so, paste that cleanup code.
4. Does the silent tool call run in parallel (spawned task) or awaited inline? If parallel, how do you synchronize access to the AudioSource?
5. Which exact installed version of livekit-agents? (output of pip show livekit-agents)
If you can’t paste code, a short pseudocode showing:
• creation -> publish -> speaking -> silent tool call -> cleanup
is enough.
Once you share this, I’ll point to the precise change (likely keeping the AudioSource alive during the silent tool call, avoiding premature close(), or adding simple synchronization) and provide the minimal code fix.white-room-9349
12/19/2025, 9:13 PMtall-airline-16756
12/19/2025, 9:14 PMAudioSource usage or capture_frame calls and that silent tool calls run in parallel. I’m responding to why the FFI error can still appear intermittently.
Likely causes and what you can try (concise):
• Internal audio sources: the Agents framework and some plugins (speech, turn-detector, TTS flows) create internal AudioSource handles for speaking/processing. Those internal handles can be created and destroyed automatically, so a parallel silent tool task can intermittently race with that lifecycle and trigger “handle not found.”
• Race between speak/start and silent tool: if a silent tool call runs while an internal source is being created or torn down, a capture request may reference a handle that briefly doesn’t exist.
Recommended quick fixes to try:
1. Keep audio pipeline alive while speaking/tool tasks run
◦ Ensure the agent’s speak/tts flow does not immediately tear down audio internals when silent tools run. If you can, reuse a persistent audio source or avoid disposing audio components until all parallel tasks complete.
1. Synchronize silent tool calls with speak lifecycle
◦ Run the silent tool inline (await instead of asyncio.create_task) or add a short await/synchronization point so tool tasks don’t start during source init/teardown.
1. Temporarily disable the turn-detector or other voice-processing plugins while debugging
◦ If you’re using turn-detector (or other plugins that capture frames), disable it to see if the error disappears. That isolates whether a plugin’s internal capture is the source.
1. Add defensive checks/retries around captures (if you control the capture path)
◦ Where a capture is triggered, retry once after a short sleep if you get “handle not found.” That mitigates transient races.
1. Confirm package/plugin versions
◦ If you haven’t already, share the exact installed versions of livekit-agents and the plugins (pip show livekit-agents livekit-plugins-turn-detector livekit-plugins-*) so I can point out known fixes.
Next step I can take for you:
• If you share which plugins are active (especially turn-detector) and the minimal async flow where speaking starts, silent tool is launched, and speaking ends, I’ll suggest the smallest code change (synchronization or persistent source) to eliminate the race.white-room-9349
12/19/2025, 9:15 PMtall-airline-16756
12/19/2025, 9:15 PMturn_detection (MultilingualModel) or other plugins (TTS/stt) create internal AudioSource handles. Parallel silent tool tasks started with asyncio.create_task() can race with those internal sources being created or torn down, causing the transient InvalidRequest("handle not found").
Recommended next steps (minimal changes to try)
1. Temporarily disable turn_detection to see if the error rate drops.
2. Run the silent tool inline (await) instead of spawning with asyncio.create_task() while debugging to eliminate the race.
3. If you need parallelism, add a small sync point around speak start/stop:
◦ Await completion of the internal speak initialization before launching silent tool tasks, or
◦ Use an asyncio.Lock() or an asyncio.Event() to prevent tool tasks from running during source init/teardown.
1. If possible, reuse a persistent audio pipeline for the agent (avoid creating/destroying internal audio sources between turns).
2. Enable a one-time retry for transient captures (sleep 10–50 ms then retry) if you can intercept the capture path.
What I need to give exact code
• Do you want a minimal code example to:
a) disable turn_detection, or b) await the silent tool instead of create_task, or c) add an `asyncio.Lock()`/`Event()` around speak/tool calls?
Tell me which option and I’ll provide the exact minimal snippet to apply.white-room-9349
12/19/2025, 9:15 PMtall-airline-16756
12/19/2025, 9:15 PMturn_detection does and why it matters for your scenario.
What turn_detection is
• It detects when a speaker’s turn starts and ends (speech turns) using audio signals or a small speech model (here you’re using MultilingualModel).
• It signals the agent when to start/stop listening, when to interrupt or yield to another speaker, and when a silence or handoff has occurred.
Why it matters for your error
• turn_detection internally interacts with audio capture/processing and can create short-lived internal AudioSource handles.
• When a silent tool runs in parallel, those internal handles may be created or torn down concurrently, which can produce the transient InvalidRequest("handle not found") you’ve seen.
When to disable or adjust it
• Disable or simplify turn detection during debugging to see if the error disappears.
• If you need parallel silent tools, keep turn detection enabled but add synchronization (await instead of create_task, an `asyncio.Lock`/`Event`, or keep audio internals alive across tasks) to avoid races.
If you want, I can show the minimal code to:
• Temporarily disable turn_detection, or
• Add a small sync (Lock/Event) so parallel silent tools won’t race with turn detection. Which would you prefer?white-room-9349
12/19/2025, 9:26 PMtall-airline-16756
12/19/2025, 9:26 PMlivekit-client): 2.16.0
To get the exact latest server or package versions now, check:
• LiveKit GitHub releases: https://github.com/livekit/livekit/releases
• PyPI for Python packages (e.g., livekit-agents): https://pypi.org/project/livekit-agents
If you want, I can check the exact installed versions in your environment — paste the output of:
• pip show livekit-agents livekit-plugins-noise-cancellation livekit-plugins-turn-detector Sources: server-sdk-go | livekit/livekit_composite | DeepWiki | Client SDKs | livekit/livekit_composite | DeepWiki