colossal-xylophone-68609
04/10/2026, 1:38 PMtall-airline-16756
04/10/2026, 1:39 PMtall-airline-16756
04/10/2026, 1:39 PMon_enter method from inside a tool.
Short answer: it’s not recommended to call on_enter manually from a tool. Calling it can work, but there’s a known risk: if on_enter awaits speech playout (for example await handle.wait_for_playout() or await self.session.generate_reply()), awaiting inside the tool call can create a circular wait/deadlock because the tool call won’t finish until on_enter returns.
What you can do instead:
• Trigger the same behavior without awaiting speech. For example, call self.session.generate_reply() but do not await it from the tool.
• Prefer letting the framework perform agent handoffs so on_enter is invoked naturally, or move the shared behavior into a helper method (safe to call from tools) and keep speech-playout/awaiting inside the normal on_enter flow.
Quick clarifying question: are you planning to call on_enter to run audio/speech playback, or just to change internal agent state? That will help me suggest the safest code pattern.
Sources: Pipeline nodes and hooks | LiveKit Documentation | Tasks and task groups | LiveKit Documentation | Agents and handoffs | LiveKit Documentation, with interactive elementscolossal-xylophone-68609
04/10/2026, 1:41 PMtall-airline-16756
04/10/2026, 1:41 PMon_enter while still awaiting generate_reply.
Short answer: not awaiting on_enter does not avoid the deadlock risk if generate_reply is awaited inside on_enter. The tool call remains active until the tool returns; if on_enter (even invoked without awaiting) schedules and then awaits generate_reply, that awaited work can still create a circular wait between the tool execution and the session/speech playout.
Safe options:
• Do not await generate_reply inside the tool or inside any call invoked from the tool. Call self.session.generate_reply() without await so playback runs independently.
• Extract non-speech logic into a helper method you can call synchronously from the tool, and reserve any awaited speech/playout calls for the normal on_enter flow that the framework triggers.
• Prefer triggering agent handoffs or session updates so the framework calls on_enter naturally rather than invoking it manually.
Quick check: do you need the reply/playout to finish before the tool returns, or can it run asynchronously? That determines whether to fire generate_reply() without awaiting or restructure the logic.
, with interactive elementscolossal-xylophone-68609
04/10/2026, 1:43 PMtall-airline-16756
04/10/2026, 1:44 PMon_enter (called from a tool) and generate_reply inside it can deadlock. Short answer: awaiting creates a circular wait because the tool call holds the execution context until it returns, and generate_reply (or TTS/playout) needs that context to progress — so both end up waiting on each other.
Why that happens, step-by-step:
1. Tool A runs and calls on_enter() (sync or via await) while Tool A’s execution context is still active.
2. on_enter() calls await session.generate_reply() (or awaits speech playout).
3. generate_reply() schedules LLM/TTS work that depends on the agent/session event loop to continue and finish.
4. The agent/session can’t progress because Tool A hasn’t returned (it’s still awaiting on_enter()), and on_enter() can’t return because it’s awaiting generate_reply() → circular wait / deadlock.
Important clarifications:
• generate_reply() does not always complete instantly; it can involve inference, TTS, and playout that require the session to move forward.
• Simply not awaiting on_enter() but still awaiting generate_reply() inside it can still produce the same blocking behavior if the tool context remains tied to that call.
Safe patterns:
• Fire-and-forget the reply inside the tool: call self.session.generate_reply(...) without await.
• Move non-speech logic into a helper you can call synchronously from the tool; keep awaited speech/playout in the normal on_enter path that the framework invokes.
• Prefer letting the framework trigger on_enter via proper handoffs rather than invoking it manually from a tool.
If you want, share a short snippet of your tool + on_enter code and I’ll point out where to change it to avoid the circular wait.
Sources: Tasks and task groups | LiveKit Documentation | Agent speech and audio | LiveKit Documentation, with interactive elementscolossal-xylophone-68609
04/10/2026, 1:48 PMtall-airline-16756
04/10/2026, 1:48 PMon_enter without awaiting it and whether that avoids problems.
Short answer: calling on_enter without await reduces the immediate blocking risk, but it can still cause issues if on_enter (or anything it triggers) contains awaited operations like generate_reply() or speech playout. Those awaited operations can create indirect blocking or unexpected ordering because they rely on the session/agent event loop that the tool call still affects.
Why this can still be a problem (step-by-step):
1. Tool starts and invokes on_enter() without awaiting it (fire-and-forget).
2. on_enter() begins running asynchronously and may call await session.generate_reply() or await TTS/playout.
3. Even though the tool didn’t await on_enter(), the session’s processing and event loop often expect the tool call to finish or yield control before progressing through response generation, TTS, or further tool calls.
4. If those downstream steps require the tool context to be released (or other parts of the session to run), they can stall or behave unpredictably because the asynchronous work was started from inside a tool context rather than via a normal handoff.
Practical consequences:
• Race conditions: the reply or playout may start later than you expect or interleave with other actions.
• Partial/no execution: parts of the async work may never complete if the session shuts down or the context is cleaned up.
• Hidden deadlocks: even without an explicit await on on_enter, the awaited work inside it can implicitly wait for resources the tool still holds.
Safe patterns instead:
• Call session.generate_reply() without awaiting it (fire-and-forget) and ensure on_enter avoids any awaits that depend on the tool context.
• Move non-async or state-only logic into a helper you call synchronously from the tool.
• Let the framework perform agent handoffs so on_enter runs in the intended context.
If you want, paste your tool.execute + on_enter snippets and I’ll point out exactly where to change things.
, with interactive elements