Outbound SIP — only 486 and 480 produce specific DisconnectReasons, everything else falls through to CLIENT_INITIATED


Environment

  • LiveKit SIP: v1.2.0 (livekit/sip:latest, image 2025-11-11)
  • livekit-agents: 1.5.1 (also reproduced on 1.4.4)
  • SIPp v3.6.1 used to drive UAS scenarios

We ran a SIPp UAS that responds with specific SIP error codes and logged the disconnect_reason on the participant in on_participant_disconnected.

SIP response: 486 Busy Here
Expected DisconnectReason: USER_REJECTED
Actual DisconnectReason: USER_REJECTED ✓
────────────────────────────────────────
SIP response: 480 Temporarily Unavailable
Expected DisconnectReason: USER_UNAVAILABLE
Actual DisconnectReason: USER_UNAVAILABLE ✓
────────────────────────────────────────
SIP response: 603 Decline
Expected DisconnectReason: USER_REJECTED
Actual DisconnectReason: CLIENT_INITIATED ✗
────────────────────────────────────────
SIP response: 408 Request Timeout
Expected DisconnectReason: USER_UNAVAILABLE
Actual DisconnectReason: CLIENT_INITIATED ✗
────────────────────────────────────────
SIP response: 404 Not Found
Expected DisconnectReason: SIP_TRUNK_FAILURE
Actual DisconnectReason: UNKNOWN_REASON ✗
────────────────────────────────────────
SIP response: 500 Server Internal Error
Expected DisconnectReason: SIP_TRUNK_FAILURE
Actual DisconnectReason: CLIENT_INITIATED ✗
────────────────────────────────────────
SIP response: No response (timeout)
Expected DisconnectReason:
CONNECTION_TIMEOUT
Actual DisconnectReason: CLIENT_INITIATED ✗


Is this the intended behaviour, or is this a known gap? The SDK already defines USER_REJECTED, USER_UNAVAILABLE, SIP_TRUNK_FAILURE, and CONNECTION_TIMEOUT in the DisconnectReason enum, and the docs describe the expected mapping — so we weren’t sure if the implementation is incomplete or if there’s a configuration we’re missing.

We’re currently unable to distinguish a 603 Decline from a normal caller hang-up, or a trunk misconfiguration (404/5xx) from any other failure. Any guidance on the intended behaviour or a workaround would be appreciated.

According to the documented behavior, your expectations are correct:

  • 603 DeclineUSER_REJECTED

  • 408 / 480USER_UNAVAILABLE

  • 5xxSIP_TRUNK_FAILURE

This mapping is explicitly defined in the Handling call outcomes table in Make outbound calls. The same page shows these reasons being surfaced via disconnect_reason in participant_disconnected.

Given that you’re seeing CLIENT_INITIATED or UNKNOWN_REASON for those cases, that does not match the documented mapping and is not expected behavior per the current docs. There is no configuration flag that alters this mapping.

As a reliable workaround today, use wait_until_answered=true and inspect the TwirpError.metadata['sip_status_code'] during CreateSIPParticipant, which preserves the upstream SIP code.

Also you can take a look at the SIP code if you want to dig deeper: GitHub - livekit/sip: SIP to WebRTC bridge for LiveKit · GitHub

1 Like