Knowledge > Runbooks > Voice Ops > Voice Agent Troubleshooting Reference
Voice Agent Troubleshooting Reference
Compiled from 4+ hours of debugging on 2026-03-27, backed by 10 research agents, 5 GitHub issues, and official LiveKit/Telnyx documentation. This is the canonical reference for diagnosing and fixing voice infrastructure problems.
Known Issues and Fixes
1. Telnyx calls return "404 No trunk found" at LiveKit
Symptom: Caller hears "number not assigned" or dead air. Telnyx SIP Call Flow Tool shows INVITE sent, LiveKit returns 404 No trunk found.
Root cause: LiveKit Cloud is a shared environment where multiple Telnyx connections may share the same IP range. Without credential-based authentication, LiveKit cannot reliably match an incoming INVITE to the correct inbound trunk.
GitHub issue: livekit/sip#592 -- Telnyx FQDN credential auth causing 407/404
Fix:
- Telnyx side: Set
user_nameandpasswordon the FQDN connection:curl -X PATCH "https://api.telnyx.com/v2/fqdn_connections/$CONNECTION_ID" \-H "Authorization: Bearer $TELNYX_API_KEY" \-H "Content-Type: application/json" \-d '{"user_name": "churchwiseai", "password": "CWA-livekit-2026!", "transport_protocol": "TCP"}' - LiveKit side: Set matching
auth_usernameandauth_passwordon the inbound trunk:/c/dev/lk.exe sip inbound update --project cwa-voice \--id ST_Xa3Bp9aixRFP \--auth-user churchwiseai \--auth-pass 'CWA-livekit-2026!'
Verification: Call the number. Check Telnyx SIP Call Flow Tool for 200 OK (not 404 or 407).
2. Agent never joins room (caller hears silence then "customer unavailable")
Symptom: Call connects, LiveKit creates a room, but the agent never joins. Caller hears silence for ~30s then gets disconnected with "customer unavailable."
Root cause: Dispatch rules created with deprecated top-level fields on CreateSIPDispatchRuleRequest. The room_config field (which specifies agent_name via RoomAgentDispatch) is silently ignored when using these deprecated fields. The room gets created but no agent dispatch happens.
GitHub issues:
- livekit/sip#401 -- Named agent dispatch broken with deprecated fields
- livekit/livekit#3690 --
room_configsilently ignored on deprecated path
Fix: Use SIPDispatchRuleInfo wrapper (field 10) instead of top-level fields:
# WRONG (deprecated, room_config silently ignored):
CreateSIPDispatchRuleRequest(
trunk_ids=[trunk_id],
rule=SIPDispatchRule(...),
name="...",
room_config=RoomConfiguration(agents=[...]), # IGNORED!
)
# CORRECT (use dispatch_rule wrapper):
CreateSIPDispatchRuleRequest(
dispatch_rule=SIPDispatchRuleInfo(
trunk_ids=[trunk_id],
rule=SIPDispatchRule(...),
name="...",
room_config=RoomConfiguration(
agents=[RoomAgentDispatch(agent_name="churchwiseai-voice")]
),
)
)
To update existing rules in-place (no gap):
await lk.sip.update_sip_dispatch_rule(rule_id, SIPDispatchRuleInfo(...))
Verification: lk.exe sip dispatch list -- the Agents column should show churchwiseai-voice.
3. "room is not connected" crash at startup
Symptom: Agent crashes intermittently with RuntimeError: room is not connected at wait_for_participant().
Root cause: wait_for_participant() requires an active room connection. If called before ctx.connect(), it crashes. In older code, session.start() auto-called ctx.connect(), but wait_for_participant() was placed before session.start().
GitHub issue: livekit/agents#4861 -- wait_for_participant room not connected
Fix: Call ctx.connect() explicitly before wait_for_participant():
@server.rtc_session(agent_name="churchwiseai-voice")
async def entrypoint(ctx: JobContext):
await ctx.connect() # MUST be first
participant = await ctx.wait_for_participant()
# ... rest of code
Remove any redundant ctx.connect() later in the function.
4. Explicit agent_name improves dispatch reliability
Symptom: Agent sometimes doesn't pick up dispatched calls even when dispatch rules look correct.
Root cause: Without an explicit agent_name on the @server.rtc_session() decorator, the agent relies on implicit matching which is unreliable for SIP-originated calls.
GitHub issue: livekit/agents#3202 -- Explicit agent_name fixes dispatch reliability
Fix: Always set agent_name on both sides:
- Decorator:
@server.rtc_session(agent_name="churchwiseai-voice") - Dispatch rule:
RoomAgentDispatch(agent_name="churchwiseai-voice")
5. Telnyx sends phone numbers without + prefix
Symptom: Phone number lookups fail because the PHONE_REGISTRY uses E.164 format (+14144007103) but Telnyx may send 14144007103 or 4144007103.
Root cause: Telnyx SIP headers may use different number formats depending on the connection's ani_number_format and dnis_number_format settings.
Reference: Telnyx SIP Number Formats
Fix: Normalize all phone numbers immediately after extraction:
if dialed_number and not dialed_number.startswith("+"):
dialed_number = "+" + dialed_number
if caller_phone and not caller_phone.startswith("+"):
caller_phone = "+" + caller_phone
Also set ani_number_format: "+E.164" and dnis_number_format: "+e164" on the Telnyx FQDN connection.
Authentication Strategy
Primary: Credential-based auth (industry standard)
Credential-based SIP authentication is the primary strategy. This is the standard approach used by production LiveKit+Telnyx integrations at scale, including Vapi and other voice AI platforms.
How it works:
- Telnyx FQDN connection has
user_name+passwordset - LiveKit inbound trunk has matching
auth_username+auth_password - When Telnyx sends SIP INVITE, LiveKit challenges with 407 Proxy Authentication Required
- Telnyx responds with credentials, LiveKit matches to the correct trunk
Current credentials:
- Username:
churchwiseai - Password:
CWA-livekit-2026! - Both Telnyx FQDN connection
2925081061861885519and LiveKit trunkST_Xa3Bp9aixRFPuse these
Fallback: IP-based auth
If credential auth fails for any reason, IP-based auth via allowed_addresses on the LiveKit trunk is the fallback. However:
- Downside: Telnyx uses shared IP ranges for FQDN connections, so IP filtering is less reliable in LiveKit Cloud's shared environment
- When to use: Only if credential auth is confirmed broken (e.g., LiveKit Cloud bug with 407 challenges)
- Implementation: Get Telnyx's SIP signaling IPs from their docs, add to
allowed_addresseson the trunk
Sources for LiveKit+Telnyx patterns
When debugging SIP issues, check these sources for proven patterns:
- Vapi (vapi.ai) -- Large-scale voice AI platform using LiveKit+Telnyx. Their integration patterns are battle-tested.
- LiveKit Official Docs:
- GitHub Issues:
- livekit/sip -- SIP-specific issues
- livekit/agents -- Agent dispatch issues
- Telnyx Support:
Operational Rules
Never delete-then-recreate trunks or dispatch rules
When updating SIP infrastructure, ALWAYS update in-place or create-then-switch-then-delete. Never delete first and create after. The gap between delete and create means incoming calls get rejected.
Safe update patterns:
- Trunks: Use
lk.exe sip inbound update --id TRUNK_ID --auth-user X --auth-pass Y - Dispatch rules (CLI): Use
lk.exe sip dispatch update --id RULE_ID ...(limited fields) - Dispatch rules (Python, full control): Use
lk.sip.update_sip_dispatch_rule(rule_id, SIPDispatchRuleInfo(...)) - If you MUST recreate: Create new resource first, update references (dispatch rules pointing to trunk), verify new resource works, THEN delete old resource.
Python API method signatures (livekit-api ~1.5)
The Python API methods have different signatures than the protobuf request objects suggest:
# Dispatch rules
lk.sip.update_sip_dispatch_rule(rule_id: str, rule: SIPDispatchRuleInfo) -> SIPDispatchRuleInfo
lk.sip.update_dispatch_rule(rule_id: str, rule: SIPDispatchRuleInfo) -> SIPDispatchRuleInfo # preferred (non-deprecated)
# Inbound trunks
lk.sip.update_sip_inbound_trunk(trunk_id: str, trunk: SIPInboundTrunkInfo) -> SIPInboundTrunkInfo
# Import paths
from livekit.protocol.room import RoomConfiguration
from livekit.protocol.agent_dispatch import RoomAgentDispatch # NOT from room!
from livekit.protocol.sip import SIPDispatchRuleInfo, SIPDispatchRule, SIPDispatchRuleIndividual
Verify after every change
After any SIP infrastructure change:
lk.exe sip dispatch list-- check Agents column is populatedlk.exe sip inbound list-- check Authentication column shows credentialslk.exe agent list-- check agent is deployed and version is current- Make a test call to verify end-to-end
Current Infrastructure (as of 2026-03-28)
| Component | ID | Details |
|---|---|---|
| Agent | CA_pX3Me4NK6qK8 | Region: us-east, agent_name: churchwiseai-voice |
| Main trunk | ST_Xa3Bp9aixRFP | 4 numbers, credential auth (churchwiseai) |
| Test trunk | ST_hvf5m2RXfEiS | +13658253552, no auth (internal testing) |
| Main dispatch | SDR_cYzx7sAkUTvx | room_prefix="call-", agent=churchwiseai-voice |
| Test dispatch | SDR_Wpyno7GDNQqg | room_prefix="test-call-", agent=churchwiseai-voice |
| Telnyx FQDN | 2925081061861885519 | LiveKit ChurchWiseAI, TCP, credential auth |
| Telnyx FQDN record | 2925083349418510207 | 5u9xu5ysoly.sip.livekit.cloud:5060 |
GitHub Issues Reference
| Issue | Summary | Status |
|---|---|---|
| livekit/sip#592 | Telnyx FQDN credential auth 407/404 in shared env | Workaround: credential auth |
| livekit/sip#401 | Named agent dispatch broken with deprecated fields | Fixed: use SIPDispatchRuleInfo wrapper |
| livekit/livekit#3690 | room_config silently ignored on deprecated path | Fixed: use dispatch_rule field 10 |
| livekit/agents#3202 | Explicit agent_name fixes dispatch reliability | Fixed: agent_name on decorator + dispatch |
| livekit/agents#4861 | wait_for_participant room not connected | Fixed: ctx.connect() before wait_for_participant |
Official Docs Reference
| Topic | URL |
|---|---|
| LiveKit Telnyx Setup Guide | https://docs.livekit.io/sip/quickstarts/configuring-telnyx-trunk/ |
| LiveKit SIP Lifecycle Recipe | https://docs.livekit.io/recipes/sip_lifecycle/ |
| Telnyx SIP Number Formats | https://support.telnyx.com/en/articles/1130706-sip-connection-number-formats |
| LiveKit SIP Trunking Docs | https://docs.livekit.io/sip/ |
| LiveKit Agent Deployment | https://docs.livekit.io/agents/deployment/ |
| LiveKit Multi-Agent Docs | https://docs.livekit.io/agents/build/multiagent/ |