Did the reference to chat context change? I'm seei...
# ask-ai
e
Did the reference to chat context change? I'm seeing
AttributeError: '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
Copy 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)
t
Thanks for reaching out, Arthur. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
You're correct to use
chat_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 docs
e
1.2 as per the demo code in quick start
t
Thanks — good to know you're on v1.2 (quickstart demo). Regarding the error you reported: you're correct that
chat_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 docs
e
This is my dummy code class GeneralClassificationAgent(Agent): def *__init__*(self) -> None: super().__init__( instructions="""You are a friendly Post Office assistant. Your role is to understand what the customer needs help with. Ask the user what they need assistance with. They may need help with: - Deliveries they are expecting - Ordering stamps and envelopes for pick up or delivery - Information about upcoming public holiday closures Once you understand their need, transfer them to the appropriate specialist using the available tools. Be warm, professional, and helpful.""", ) async def *on_enter*(self) -> None: await self.session.generate_reply( instructions="Greet the user warmly and ask how you can help them today at the Post Office." ) @function_tool() async def *transfer_to_delivery*(self, _context_: RunContext) -> tuple[Agent, str]: """Transfer the customer to the delivery specialist for inquiries about packages and deliveries.""" return DeliveryAgent(_chat_ctx_=self.session.chat_ctx) @function_tool() async def *transfer_to_ordering*(self, _context_: RunContext) -> tuple[Agent, str]: """Transfer the customer to the ordering specialist for purchasing stamps and envelopes.""" return OrderingAgent(_chat_ctx_=self.session.chat_ctx) @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)
t
Thanks — I looked at your dummy code and the error cause is clear. What I'm responding to: your
transfer_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:
Copy code
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:
Copy code
@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 docs
e
Does context get preserved if I use your method? I'm reading from your docks that it doesn't in https://docs.livekit.io/agents/build/agents-handoffs/
t
You're right to check the handoffs doc — that page is correct. Short answer: context is not automatically preserved if you only return a new Agent instance. To ensure the new agent receives the conversation history, pass the session's chat context (or a filtered copy) when constructing the new agent and return the required tuple (Agent, str). What to do: 1. Return the tuple (Agent, str). 2. Pass
chat_ctx
(or a copied/filtered version) when you need the new agent to see prior messages. Example:
Copy code
@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.
e
Looking at the slack history it seems the correct answer is to use .history.copy() - is that correct?
t
Yes — use
session.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:
Copy code
@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