User_state stuck in speaking during the agent handoff

We are using livekit workflow multi-agent framework.

We noticed that during the agent transfer, if the user is speaking the state is stuck in speaking.
it never changes to listening after user silence or after agent handoff.

Attaching the transfer_to_agent fn definition,

@function_tool
async def transfer_to_agent(self, target_agent_id: str, run_ctx: RunContext):
“”"
Transfer to another agent.
“”"

# Disable interruptions during transfer
run_ctx.disallow_interruptions()


# Create new chat context (optional)
new_chat_ctx = self.chat_ctx.copy() if self.chat_ctx else None

# Create next agent
factory = self.agents_map[target_agent_id]
next_agent = await factory({"source_agent_id": self._agent_id})

# Apply chat context
if new_chat_ctx:
    await next_agent.update_chat_ctx(
        new_chat_ctx,
        exclude_invalid_function_calls=False
    )

# Pause current activity scheduling to avoid race conditions
if (
    self.agent_session
    and hasattr(self.agent_session, "_activity")
    and self.agent_session._activity
):
    self.agent_session._activity._scheduling_paused = True

return next_agent, None

livekit-agents = {version = "1.3.9” }

During a handoff, agent and user states are driven by VAD and the active AgentSession. If you call run_ctx.disallow_interruptions() and manually pause _activity._scheduling_paused, you can prevent the session from completing the current turn cycle, which blocks transitions like speaking → listening.

Instead of pausing internal _activity (and I would discourage accessing any internal objects, denoted by the _), let the framework manage the lifecycle and use the built‑in handoff flow.

# Pause current activity scheduling to avoid race conditions

Clearly this code is working around some other issue, I think fixing the race condition that prompted you to add this line is probably the best way forward.

self.agent_session._activity._scheduling_paused = True this was added to solve the below problem -LLM emitted { message , tool } in same turn, the tool was transfer_to_agent tool.
Thought the function_tool is shown success in langfuse, the user interrupted while the “message“ was being spoken.

The current agent activity drained but the new agent activity didn’t come up.
So by adding this line, it solved that problem.