early-butcher-66889
12/16/2025, 6:01 AMAttributeError: 'AgentSession' object has no attribute 'chat_ctx'. Did you mean: '_chat_ctx'? errors when I try to pass in context from one agent to another - sample code
@function_tool()
async def transfer_to_holiday_info(self, context: RunContext) -> tuple[Agent, str]:
"""Transfer the customer to get information about public holiday closures."""
return PublicHolidayClosuresAgent(chat_ctx=self.session.chat_ctx)tall-airline-16756
12/16/2025, 6:01 AMtall-airline-16756
12/16/2025, 6:01 AMchat_ctx when handing off context — chat_ctx is the public property for preserving conversation history and is what agents expect when you pass context to a new agent.
The AttributeError you saw usually means self.session (or the Session object you have) doesn't yet expose chat_ctx in that runtime/version. Two quick checks to narrow this down:
1. What SDK version are you running? If it's not the latest release, please upgrade — recent releases expose chat_ctx for handoffs.
2. Confirm the lifecycle moment you're accessing it: is self.session fully initialized when the function runs? Add a quick guard to log/inspect dir(self.session) or hasattr(self.session, "chat_ctx") before using it.
If you want, paste your SDK version and where this function runs (during init, inside a handler, etc.) and I’ll suggest the exact next step.
Sources: Agents and handoffs | LiveKit docsearly-butcher-66889
12/16/2025, 6:02 AMtall-airline-16756
12/16/2025, 6:03 AMchat_ctx is the public property to pass conversation context between agents. The AttributeError means chat_ctx isn't present on the object at the moment you're accessing it.
Checklist to resolve this quickly:
1. Ensure you are not accessing chat_ctx in the agent's __init__. Access it only after the session has started (for example inside on_enter, a handler, or after await session.start(...) completes).
2. Confirm you're using the session object that has been started (use hasattr(self.session, "chat_ctx") or dir(self.session) to verify at runtime).
3. If this runs inside a function tool before the session is started, pass the session or context later when the session is active.
If you share where transfer_to_holiday_info is being invoked (during init, on_enter, or a user turn) I’ll point to the exact place to move the call or show a small corrected snippet. Would you like that?
Sources: Agents v0.x migration guide - Node.js | LiveKit Docs | Agents and handoffs | LiveKit docsearly-butcher-66889
12/16/2025, 6:03 AMtall-airline-16756
12/16/2025, 6:04 AMtransfer_to_* functions return only an Agent instance and access self.session.chat_ctx, which is causing the AttributeError during handoff.
What to do:
1. Change each tool to return a tuple (Agent, str) — the second element is a handoff message. Example:
return DeliveryAgent(), "Transferring to delivery specialist."
1. You do not need to pass chat_ctx=self.session.chat_ctx for normal handoffs. The AgentSession preserves conversation history and will provide the new agent the proper context.
2. Ensure you access chat_ctx only after the session has started (e.g., inside on_enter, handlers, or after await session.start(...)). If you must manipulate context, use self.session.chat_ctx.copy(...) and pass that copy with the tuple return.
Quick corrected snippet for your three tools:
@function_tool()
async def transfer_to_delivery(self, context: RunContext) -> tuple[Agent, str]:
return DeliveryAgent(), "Transferring to delivery specialist."
@function_tool()
async def transfer_to_ordering(self, context: RunContext) -> tuple[Agent, str]:
return OrderingAgent(), "Transferring to ordering specialist."
@function_tool()
async def transfer_to_holiday_info(self, context: RunContext) -> tuple[Agent, str]:
return PublicHolidayClosuresAgent(), "Providing holiday closure information."
If you still see the AttributeError after this change, tell me:
• Where exactly the tool is being invoked (during on_enter, a user turn, or elsewhere).
• The class of self.session and whether await session.start(...) has completed.
I can then point to the exact lifecycle fix or show a test flow. Would you like that?
Sources: Agents and handoffs | LiveKit docs | Agent-assisted warm transfer | LiveKit docs | Pipeline nodes and hooks | LiveKit docs | Agents v0.x migration guide - Python | LiveKit docsearly-butcher-66889
12/16/2025, 6:05 AMtall-airline-16756
12/16/2025, 6:05 AMchat_ctx (or a copied/filtered version) when you need the new agent to see prior messages.
Example:
@function_tool()
async def transfer_to_delivery(self, context: RunContext) -> tuple[Agent, str]:
filtered = self.session.chat_ctx.copy(
exclude_instructions=True,
exclude_function_call=True,
exclude_empty_message=True
)
return DeliveryAgent(chat_ctx=filtered), "Transferring to delivery specialist."
If you want the full, minimal-history or full-history options, tell me which one you prefer and I’ll provide the exact snippet for that flow.early-butcher-66889
12/16/2025, 6:11 AMtall-airline-16756
12/16/2025, 6:11 AMsession.history.copy() to preserve the full conversation across an agent handoff.
Why:
• session.history contains the complete, accumulated session history (all user messages, agent replies, tool calls, etc.).
• session.history.copy() creates a ChatContext with that full history so the new agent sees prior turns and avoids repetition.
• session.chat_ctx is the current agent's local context and may not include earlier session messages if context was filtered or reset.
Do this and return the required tuple (Agent, str). Example:
@function_tool()
async def transfer_to_delivery(self, context: RunContext) -> tuple[Agent, str]:
return DeliveryAgent(chat_ctx=self.session.history.copy()), "Transferring to delivery specialist."
Would you like a variant that filters out instructions or function calls from the copied history?
Sources: Agents and handoffs | LiveKit docs