wooden-king-32027
03/11/2026, 2:16 PMtall-airline-16756
03/11/2026, 2:17 PMjob_request.reject()).
Right now, LiveKit does not provide a single built-in event labeled “participant declined.” Here’s what you can detect and how to infer a decline for each scenario:
• Invitation declined (app-level): Implement an explicit application event. When you show an invite, have the client send an “invite_declined” API call or emit a signal/webhook back to your server when the user presses Decline. This is the most reliable approach for “decline” as a business action.
• Browser permission denial or user closed page before connecting: You will not see a participantConnected event. Detect this client-side and inform your server:
◦ On the client, listen for permission errors or page unload and send a signal/HTTP notification that the user did not join.
◦ Server-side, treat absence of a join within an expected time window as a “no-show” if you need timeouts.
• SIP/telephony rejection: SIP call lifecycles expose call status (answered, ringing, rejected). If you use SIP integration, inspect the SIP call status (sip.callStatus) or call-signaling events to detect rejected/declined responses.
• Agent/job rejection workflows: If you use LiveKit Agents and jobs, a rejection via job_request.reject() is an explicit event in that workflow; handle that event in your job/agent logic.
Events you can rely on
• participantConnected — emitted when a participant successfully connects.
• participantDisconnected — when a participant leaves after joining.
• Webhooks — participant_joined is sent when a participant connects; there is no built-in participant_declined webhook.
Next step — tell me which scenario applies Pick which of the above matches your use case (or describe a different flow). I’ll provide concrete implementation suggestions (client code, webhook flow, or SIP/agent signals) you can add to reliably detect and record a “decline.”
Sources: client-sdk-flutter | livekit/livekit_composite | DeepWiki | Best practices for managing webhook event streams | LiveKit | SIP Lifecycle Management Agent | LiveKit Documentation | Why do jobs stop after agent rejection? | LiveKitwooden-king-32027
03/28/2026, 12:37 PMtall-airline-16756
03/28/2026, 12:37 PMchat_ctx explicitly when constructing the next agent, or relying on session state only?
• Are you using any custom userdata or non-serializable objects in the chat context (files, open sockets, complex objects)?
How context transfer works (short)
• Context is not automatically carried to a new agent. You must pass the conversation context explicitly using chat_ctx (or share session userdata) when creating the next agent.
• The recommended pattern is to pass a copy of the context and optionally exclude system instructions so the new agent doesn’t inherit unwanted instructions.
What to try (step-by-step)
1. Pass a copy of chat context when creating the new agent
◦ Use a copy to avoid mutating the previous agent’s state:
# Python example
return NextAgent(chat_ctx=self.chat_ctx.copy(exclude_instructions=True)), "Transferring to NextAgent"
• exclude_instructions=True prevents the previous agent’s system prompt/instructions from being passed if that’s unwanted.
1. If the target agent must receive the full history, pass the full copy:
return NextAgent(chat_ctx=self.chat_ctx.copy()), "Transferring with full context"
1. If conversation history is large, summarize before transferring
◦ Create a short summary using an LLM call and pass the summary in chat_ctx to avoid token/input size errors.
1. Check types in chat_ctx
◦ Ensure chat_ctx contains serializable primitives/strings/structured messages. Non-serializable objects can cause input errors when the new agent is instantiated.
1. Use session userdata for shared structured state
◦ If you have custom state you want available to all agents, set it on session.userdata (it’s accessible across agents in the same session).
1. Force the handoff in a function tool (if applicable)
◦ The handoff pattern is commonly used inside @function_tool methods where you return the new agent and an optional message. Ensure your function returns exactly the expected tuple or structure your framework expects.
Common reasons for “input error” during transfer
• Passing an unsupported or too-large object in chat_ctx.
• Not using .copy() and the new agent tries to mutate the original; the agent constructor or verifier rejects it.
• The target agent expects a different context schema (different fields or types).
• Token/size limits — very long histories can cause downstream LLM input rejections.
If the error persists — what to share
• The exact error message and stack trace.
• A short code snippet showing how you construct and call the transfer (sanitize keys/secrets).
• A sample of what chat_ctx contains (structure and types).
• Whether you’re using any custom tools or function schemas that the new agent must receive.
I can then:
• Point out the exact line/config that’s causing the input error, or
• Show a corrected transfer snippet for your Nova 2 Sonic agent flow, or
• Suggest a summarization helper if this is a size/token issue.
Which of the quick questions above can you answer now? If you paste the error and the transfer code snippet, I’ll review it and give a specific fix.
Sources: Agents and handoffs | LiveKit Documentation, with interactive elements