I am getting this error - ERROR livekit [correlati...
# ask-ai
q
I am getting this error - ERROR livekit [correlation_id: 964798169]: failed to emit event conversation_item_added Traceback (most recent call last): File "/opt/copart/ai-call-bot/venv/lib/python3.12/site-packages/livekit/rtc/event_emitter.py", line 58, in emit callback(*callback_args) File "/opt/copart/ai-call-bot/entrypoint.py", line 412, in _on_conversation_item_added if event.item.role == "user" and user_speech_start_time: ^^^^^^^^^^^^^^^ File "/opt/copart/ai-call-bot/venv/lib/python3.12/site-packages/pydantic/main.py", line 991, in getattr raise AttributeError(f'{type(self).name!r} object has no attribute {item!r}') AttributeError: 'AgentHandoff' object has no attribute 'role'
✅ 1
t
Thanks for reaching out, Harsh. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I can see the error is raised because your handler expects
event.item
to have a
role
, but the item is an
AgentHandoff
which has no
role
. I'll help you fix that. What I recommend: • Update
_on_conversation_item_added
to check the item
type
before accessing
role
. • Only access
role
for
message
items; handle or ignore other types (like
agent_handoff
) explicitly. Example safe pattern:
Copy code
def _on_conversation_item_added(event):
    # ignore non-message items which don't have `role`
    if getattr(event.item, "type", None) != "message":
        return

    if event.item.role == "user" and user_speech_start_time:
        # existing logic
        ...
If you want, tell me: • The current structure of
event.item
(keys you observe), and • Any special handling you want for
agent_handoff
(e.g., logging, state update), and I’ll give a tailored code snippet. Sources: Chat context | LiveKit Documentation | How to Build a Background Observer for Voice AI Guardrails | LiveKit | How to Build a Background Observer for Voice AI Guardrails | LiveKit, with interactive elements
q
this is my current code of conversation item added -
Copy code
@session.on("conversation_item_added")
        def _on_conversation_item_added(event: ConversationItemAddedEvent):
            nonlocal user_speech_start_time, agent_speech_start_time

            item_end_time = event.created_at - call_start_time

            if event.item.role == "user" and user_speech_start_time:
                item_start_time = user_speech_start_time - call_start_time
                user_speech_start_time = None
            elif event.item.role == "assistant" and agent_speech_start_time:
                item_start_time = agent_speech_start_time - call_start_time
                agent_speech_start_time = None
            else:
                item_start_time = item_end_time

            <http://log.info|log.info>(f"[{agent_name}][correlation_id: {agent.room_name}]: {event.item.role} - {event.item.content} message from {item_start_time:.2f}s to {item_end_time:.2f}s")

            conversation_item = {
                "id": event.item.id,
                "type": event.item.type,
                "role": event.item.role,
                "content": event.item.content,
                "interrupted": event.item.interrupted,
                "transcript_confidence": event.item.transcript_confidence,
                "start_time": round(item_start_time, 2),
                "end_time": round(item_end_time, 2)
            }
            conversation_items.append(conversation_item)
t
Thanks — this is helpful. The error happens because some items (like
agent_handoff
) don’t have
role
. Update the handler to check
event.item.type
(or guard attribute access) before reading
role
and
content
. Suggested patch — safe checks and explicit handling for non-message items:
Copy code
@session.on("conversation_item_added")
def _on_conversation_item_added(event: ConversationItemAddedEvent):
    nonlocal user_speech_start_time, agent_speech_start_time

    item_type = getattr(event.item, "type", None)
    item_id = getattr(event.item, "id", None)
    item_end_time = event.created_at - call_start_time

    # Only message items have `role` and `content`
    if item_type == "message":
        role = event.item.role
        content = event.item.content
        if role == "user" and user_speech_start_time:
            item_start_time = user_speech_start_time - call_start_time
            user_speech_start_time = None
        elif role == "assistant" and agent_speech_start_time:
            item_start_time = agent_speech_start_time - call_start_time
            agent_speech_start_time = None
        else:
            item_start_time = item_end_time

        log.info(f"[{agent_name}][correlation_id: {agent.room_name}]: {role} - {content} message from {item_start_time:.2f}s to {item_end_time:.2f}s")

        conversation_item = {
            "id": item_id,
            "type": item_type,
            "role": role,
            "content": content,
            "interrupted": getattr(event.item, "interrupted", None),
            "transcript_confidence": getattr(event.item, "transcript_confidence", None),
            "start_time": round(item_start_time, 2),
            "end_time": round(item_end_time, 2)
        }
    else:
        # Handle or ignore other item types (agent_handoff, function_call, etc.)
        item_start_time = item_end_time
        log.info(f"[{agent_name}][correlation_id: {agent.room_name}]: {item_type} item (id={item_id}) at {item_end_time:.2f}s")
        conversation_item = {
            "id": item_id,
            "type": item_type,
            "start_time": round(item_start_time, 2),
            "end_time": round(item_end_time, 2),
        }

        # Optional: capture agent_handoff details if present
        if item_type == "agent_handoff":
            conversation_item.update({
                "old_agent_id": getattr(event.item, "old_agent_id", None),
                "new_agent_id": getattr(event.item, "new_agent_id", None),
            })

    conversation_items.append(conversation_item)
If you want, tell me whether you prefer to: • Ignore non-message items entirely, or • Log/store extra fields for specific types like
agent_handoff
(and which fields), and I’ll adjust the snippet accordingly. , with interactive elements
q
I am also getting this warning - WARNING livekit.agents [correlation_id: 86842379]: min_endpointing_delay, max_endpointing_delay, preemptive_generation, turn_detection are deprecated and will be removed in v2.0. Use turn_handling=TurnHandlingOptions(...) instead This is my agent session code -
Copy code
session = AgentSession(
    userdata=userdata,
    turn_handling=TurnHandlingOptions(
        
    ),
    turn_detection=MultilingualModel(),
    use_tts_aligned_transcript=False,
    user_away_timeout= None, #agent_config['agent']['user_away_timeout'],
    vad=ctx.proc.userdata["vad"],
    min_endpointing_delay=agent_config['agent']['min_endpointing_delay'],
    max_endpointing_delay=agent_config['agent']['max_endpointing_delay'],
    preemptive_generation=agent_config['agent']['preemptive_generation'],
    ivr_detection=agent_config['agent']['ivr_detection'],
    stt=stt.FallbackAdapter(
        stt=[
            deepgram.STT(
                model=agent_config['primary_stt']['model'],
                interim_results=agent_config['primary_stt']['interim_results'],
                smart_format=agent_config['primary_stt']['smart_format'],
                punctuate=agent_config['primary_stt']['punctuate'],
                filler_words=agent_config['primary_stt']['filler_words'],
                profanity_filter=agent_config['primary_stt']['profanity_filter'],
                language=agent_config['primary_stt']['language'],
                numerals=agent_config['primary_stt']['numerals'],
                api_key=config['DEEPGRAM_API_KEY']
            ),
            google.STT(
                model=agent_config['secondary_stt']['model'],
                credentials_file=os.getenv("GOOGLE_APPLICATION_CREDENTIALS"),
                location=agent_config['secondary_stt']['location']
            )
        ]
    ),
    llm=selected_llm,
    tts=selected_tts,
)
t
Thanks — I see two things to fix: (1) guard against non-message items (like
agent_handoff
) in
conversation_item_added
to avoid AttributeError, and (2) move deprecated session args into
TurnHandlingOptions
. 1) Patch for conversation_item_added (only access `role`/`content` for
message
items):
Copy code
@session.on("conversation_item_added")
def _on_conversation_item_added(event: ConversationItemAddedEvent):
    nonlocal user_speech_start_time, agent_speech_start_time

    item_type = getattr(event.item, "type", None)
    item_id = getattr(event.item, "id", None)
    item_end_time = event.created_at - call_start_time

    if item_type == "message":
        role = event.item.role
        content = event.item.content
        if role == "user" and user_speech_start_time:
            item_start_time = user_speech_start_time - call_start_time
            user_speech_start_time = None
        elif role == "assistant" and agent_speech_start_time:
            item_start_time = agent_speech_start_time - call_start_time
            agent_speech_start_time = None
        else:
            item_start_time = item_end_time

        log.info(f"[{agent_name}][correlation_id: {agent.room_name}]: {role} - {content} message from {item_start_time:.2f}s to {item_end_time:.2f}s")

        conversation_item = {
            "id": item_id,
            "type": item_type,
            "role": role,
            "content": content,
            "interrupted": getattr(event.item, "interrupted", None),
            "transcript_confidence": getattr(event.item, "transcript_confidence", None),
            "start_time": round(item_start_time, 2),
            "end_time": round(item_end_time, 2),
        }
    else:
        # handle or ignore non-message items (agent_handoff, function_call, etc.)
        item_start_time = item_end_time
        log.info(f"[{agent_name}][correlation_id: {agent.room_name}]: {item_type} item (id={item_id}) at {item_end_time:.2f}s")
        conversation_item = {
            "id": item_id,
            "type": item_type,
            "start_time": round(item_start_time, 2),
            "end_time": round(item_end_time, 2),
        }
        if item_type == "agent_handoff":
            conversation_item.update({
                "old_agent_id": getattr(event.item, "old_agent_id", None),
                "new_agent_id": getattr(event.item, "new_agent_id", None),
            })

    conversation_items.append(conversation_item)
2) Replace deprecated AgentSession args by configuring
turn_handling
. Move
turn_detection
, endpointing delays, and
preemptive_generation
into `TurnHandlingOptions`:
Copy code
session = AgentSession(
    userdata=userdata,
    turn_handling=TurnHandlingOptions(
        turn_detection=MultilingualModel(),
        preemptive_generation=agent_config['agent']['preemptive_generation'],
        endpointing={
            "min_delay": agent_config['agent']['min_endpointing_delay'],  # seconds in Python
            "max_delay": agent_config['agent']['max_endpointing_delay'],
        },
    ),
    use_tts_aligned_transcript=False,
    user_away_timeout=None,
    vad=ctx.proc.userdata["vad"],
    ivr_detection=agent_config['agent']['ivr_detection'],
    stt=stt.FallbackAdapter(
        stt=[ ... ]  # your existing STT setup
    ),
    llm=selected_llm,
    tts=selected_tts,
)
If you want, tell me whether you prefer to: • ignore non-message items entirely, or • record extra fields for types like
agent_handoff
(and which fields), and I’ll return a tighter snippet for that preference. Sources: https://docs.livekit.io/agents/logic/sessions.md| | Agent session | LiveKit Documentation | LiveKit turn detector plugin | LiveKit Documentation | How to Build a Background Observer for Voice AI Guardrails | LiveKit | How to Build a Background Observer for Voice AI Guardrails | LiveKit, with interactive elements
q
I am getting this warning as well - RoomInputOptions and RoomOutputOptions are deprecated, use RoomOptions instead
t
Thanks — I'll address the three issues you reported and show concise fixes. 1) AttributeError in conversation_item_added • Problem: some items (e.g.,
agent_handoff
) don’t have
role
or
content
. • Fix: only access `role`/`content` when `event.item.type == "message"`; handle other types separately. Suggested snippet (replace your handler with this):
Copy code
@session.on("conversation_item_added")
def _on_conversation_item_added(event: ConversationItemAddedEvent):
    nonlocal user_speech_start_time, agent_speech_start_time

    item_type = getattr(event.item, "type", None)
    item_id = getattr(event.item, "id", None)
    item_end_time = event.created_at - call_start_time

    if item_type == "message":
        role = event.item.role
        content = event.item.content
        if role == "user" and user_speech_start_time:
            item_start_time = user_speech_start_time - call_start_time
            user_speech_start_time = None
        elif role == "assistant" and agent_speech_start_time:
            item_start_time = agent_speech_start_time - call_start_time
            agent_speech_start_time = None
        else:
            item_start_time = item_end_time

        log.info(f"[{agent_name}][correlation_id: {agent.room_name}]: {role} - {content} message from {item_start_time:.2f}s to {item_end_time:.2f}s")

        conversation_item = {
            "id": item_id,
            "type": item_type,
            "role": role,
            "content": content,
            "interrupted": getattr(event.item, "interrupted", None),
            "transcript_confidence": getattr(event.item, "transcript_confidence", None),
            "start_time": round(item_start_time, 2),
            "end_time": round(item_end_time, 2),
        }
    else:
        # handle or ignore non-message items
        item_start_time = item_end_time
        log.info(f"[{agent_name}][correlation_id: {agent.room_name}]: {item_type} item (id={item_id}) at {item_end_time:.2f}s")
        conversation_item = {
            "id": item_id,
            "type": item_type,
            "start_time": round(item_start_time, 2),
            "end_time": round(item_end_time, 2),
        }
        if item_type == "agent_handoff":
            conversation_item.update({
                "old_agent_id": getattr(event.item, "old_agent_id", None),
                "new_agent_id": getattr(event.item, "new_agent_id", None),
            })

    conversation_items.append(conversation_item)
2) Deprecated turn-related args warning • Problem:
turn_detection
,
min_endpointing_delay
,
max_endpointing_delay
, and
preemptive_generation
must be moved into
TurnHandlingOptions
. • Fix: set those options inside
turn_handling
. In Python
endpointing
delays are in seconds. Example change:
Copy code
session = AgentSession(
    userdata=userdata,
    turn_handling=TurnHandlingOptions(
        turn_detection=MultilingualModel(),
        preemptive_generation=agent_config['agent']['preemptive_generation'],
        endpointing={
            "min_delay": agent_config['agent']['min_endpointing_delay'],
            "max_delay": agent_config['agent']['max_endpointing_delay'],
        },
    ),
    use_tts_aligned_transcript=False,
    user_away_timeout=None,
    vad=ctx.proc.userdata["vad"],
    ivr_detection=agent_config['agent']['ivr_detection'],
    stt=stt.FallbackAdapter(stt=[ ... ]),
    llm=selected_llm,
    tts=selected_tts,
)
3) RoomInputOptions/RoomOutputOptions deprecation • Problem: those classes are deprecated; use unified
RoomOptions
instead. • Fix: replace any use of `RoomInputOptions`/`RoomOutputOptions` with
RoomOptions
and provide the same input/output configuration there. If you paste the current RoomInput/RoomOutput usage, I can give the exact
RoomOptions
mapping. Next step • Do you want me to: ◦ produce a full patched file with these changes applied, or ◦ convert specific RoomInput/RoomOutput code to
RoomOptions
if you paste it now? Sources: https://docs.livekit.io/agents/logic/sessions.md| | Agent session | LiveKit Documentation, with interactive elements
q
Copy code
await session.start(
    agent=agent,
    room=ctx.room,
    room_input_options=RoomInputOptions(
        noise_cancellation=noise_cancellation.BVCTelephony(),
        text_enabled=False,
    ),
)
t
Thanks — replace
room_input_options
with
room_options
using
RoomOptions
. In Python the field names changed (
text_enabled
→
text_input
). Example patch:
Copy code
from livekit.agents import room_io
from livekit.plugins import noise_cancellation

await session.start(
    agent=agent,
    room=ctx.room,
    room_options=room_io.RoomOptions(
        noise_cancellation=noise_cancellation.BVCTelephony(),
        text_input=False,
    ),
)
This removes the deprecation warning. If you also use any
RoomOutputOptions
, move those settings into the same
RoomOptions
. Paste your current input/output settings if you want a direct one-to-one mapping. Sources: Agent session | LiveKit Documentation | Turns overview | LiveKit Documentation | How to Build a Background Observer for Voice AI Guardrails | LiveKit, with interactive elements
q
i can't pass noise_cancellation parameter inside RoomOptions
t
You're correct —
noise_cancellation
isn't a top-level field on
RoomOptions
. Put it under
audio_input
using
AudioInputOptions
. Example:
Copy code
from livekit.agents import room_io
from livekit.plugins import noise_cancellation

await session.start(
    agent=agent,
    room=ctx.room,
    room_options=room_io.RoomOptions(
        audio_input=room_io.AudioInputOptions(
            noise_cancellation=noise_cancellation.BVCTelephony()
        ),
        text_input=False,
    ),
)
This moves the noise cancellation into the correct place and removes the error. If you want, paste any other RoomInput/RoomOutput fields you used and I’ll map them into
RoomOptions
. Sources: https://docs.livekit.io/agents/logic/sessions.md| | Google AI and LiveKit | LiveKit Documentation | Agent session | LiveKit Documentation, with interactive elements
q
I am getting this error - amd closed before a result was available
t
Sorry you're hitting that — I can help diagnose "amd closed before a result was available", but I need a bit more context. Could you paste: • The full error traceback (complete message and stack trace). • Which operation triggers it (during session.start, STT/LLM call, session.stop, etc.). • Any recent changes (switching RoomOptions/TurnHandlingOptions, noise cancellation, or STT adapter). • The SDK version you’re using (pip show livekit or package version). With that I’ll suggest targeted fixes. , with interactive elements