Environment:
- livekit-server 1.9.2 (self-hosted, Docker, host networking)
- livekit-sip v1.6.0 (self-hosted, Docker, same host)
- Redis shared between both services (localhost:6379), confirmed matching
config on both sides - SIP provider: Telnyx, IP-authenticated Connection (no registration),
Italian DID
Symptom:
Every real inbound call from Telnyx is silently swallowed. livekit-sip logs “Rejecting inbound flood” (case DispatchNoRuleDrop in pkg/sip/inbound.go) even though the trunk is correctly matched (sipTrunk present in the log) and the trunk/dispatch rule configuration is verified correct via the API AND directly in Redis (raw HGET on sip_inbound_trunk / sip_dispatch_rule hashes matches the API output exactly, byte for byte).
Critical detail found via packet capture (tcpdump on port 5060):
livekit-sip sends 100 Trying + 180 Ringing BEFORE evaluating the dispatch rule (StartRinging() is called before DispatchCall() in handleInvite).
When dispatch evaluation then returns DispatchNoRuleDrop, the code calls cc.Drop() — which does NOT send any SIP response at all. No 486, no BYE, nothing. The call just rings forever from the caller’s perspective; the
“status”: 486 field in the JSON log is purely an internal bookkeeping value, not something actually transmitted. In our testing the call kept ringing until we manually hung up (confirmed via tcpdump — CANCEL only
appears after we hang up, ~30s+ into the call, never triggered by our side).
What I’ve ruled out so far (fresh test call after each change):
- Number format (with/without leading “+”) — fixed and verified against the actual “To” user seen in the SIP INVITE.
- Incomplete IP allowlist — expanded allowed_addresses to all 12 documented Telnyx signaling IPs across all regions. Confirmed both a US (192.76.120.10) and EU (185.246.41.140, host sip.telnyx.eu) signaling
IP hit our trunk correctly (sipTrunk resolved) and both still get DispatchNoRuleDrop. - Stale in-memory state in livekit-sip — restarted the container (new nodeID confirmed), no change.
- Stale in-memory state in livekit-server — restarted that container too (workers re-registered automatically within seconds), no change.
- Ambiguous match between two dispatch rules sharing the same room_prefix on different trunks — temporarily deleted the unrelated trunk’s dispatch rule, no change.
- Dispatch rule type — tried both SIPDispatchRuleIndividual and SIPDispatchRuleDirect, identical rejection in both cases.
- Update vs. create code path — suspected that UpdateSIPInboundTrunk might not refresh whatever internal index the live dispatch matcher uses (as opposed to the raw hash we could read directly). Deleted and recreated both the trunk and the dispatch rule from scratch (create only, no update calls at all) with a brand new trunk ID — identical rejection.
- Directly inspected Redis (
HGET sip_inbound_trunk <id>/HGET sip_dispatch_rule <id>) immediately before a test call: the raw stored protobuf matches the trunk numbers, allowed_addresses, dispatch rule
trunk_ids/inbound_numbers/room_config.agents exactly as reported by the admin API. No discrepancy between storage and what the API reports.
Enabled debug logging (note: the correct config key is the nested logging: {level: debug}, not a top-level log_level key from older examples, which is silently ignored) to confirm the exact code path is
consistently case DispatchNoRuleDrop in inbound.go, meaning livekit-server’s dispatch evaluation itself returns “no matching rule, drop” for this call, despite everything we can inspect (API + Redis)
being correct.
So, my questions are:
- Is there a known cause for DispatchNoRuleDrop being returned when the trunk/dispatch rule configuration is verifiably correct in both the API and the underlying Redis storage?
- Separately from the root cause: is
cc.Drop()on DispatchNoRuleDrop intended to skip sending any final SIP response even after a 180 Ringing was already sent? That seems like a bug in its own right regardless of why dispatch evaluation fails — it leaves the caller’s phone ringing indefinitely instead of failing fast.
Happy to share full debug logs, a redacted pcap, or run additional diagnostics live if that helps track this down.
Thanks all