WebRTC Android SDK Error Handling
This document describes the error handling mechanisms and call state event details in the Telnyx WebRTC Android SDK. It is divided into a Reference section detailing possible errors and states, and a Guide section on how to consume and manage them.
Error & Call State Reference
This section provides a reference for the various error conditions and call states you might encounter when using the SDK.
1. Socket-Level Errors (via SocketResponse
)
These errors are typically reported through the TelnyxClient.socketResponseLiveData
when SocketResponse.status
is SocketStatus.ERROR
. The SocketResponse
for errors now includes an optional errorCode: Int?
field in addition to the errorMessage: String?
.
- Gateway Registration Issues:
errorMessage
: "Gateway registration has timed out",errorCode
: -32003 (Triggered if gateway status is not "REGED" e.g.,GatewayState.NOREG
, orGatewayState.EXPIRED
).errorMessage
: "Gateway registration has failed",errorCode
: -32004 (Triggered if gateway status is "FAILED" or results inGatewayState.FAIL_WAIT
).
- WebSocket Error Messages from Server:
- The
errorMessage
will contain the server-provided message. - The
errorCode
will contain the server-provided error code (e.g., -32000 for token error, -32001 for credential error) if available in the JSON payload from the server.
- The
- No Network Connection (on initial connect):
errorMessage
: "No Network Connection",errorCode
:null
(Triggered if an attempt to connect is made when the device has no active network connection).
- Client-Side Reconnection Timeout:
errorMessage
: "Reconnection timeout after X seconds",errorCode
:null
(Triggered if the SDK's internal reconnection timer expires).
2. Call-Specific States & Reasons (via Call.callStateFlow
)
Individual Call
objects emit their state changes through callStateFlow
. Several states now include detailed reasons:
CallState.DROPPED(reason: CallNetworkChangeReason)
:- Indicates a call was dropped, usually due to network problems.
reason
(of typeCallNetworkChangeReason
) provides context:CallNetworkChangeReason.NETWORK_LOST
: Network connectivity was completely lost.CallNetworkChangeReason.NETWORK_SWITCH
: A network switch occurred (e.g., Wi-Fi to Cellular), and while reconnection might be attempted, this state can be hit if it ultimately fails in that context, or if it's a direct drop without a reconnect attempt.
CallState.RECONNECTING(reason: CallNetworkChangeReason)
:- The SDK is attempting to reconnect a call after a network disruption.
reason
(of typeCallNetworkChangeReason
) provides context:CallNetworkChangeReason.NETWORK_SWITCH
: Typically seen when the SDK tries to recover a call after a network handover.
CallState.DONE(reason: CallTerminationReason?)
:- The call has ended. The optional
reason
parameter (of typeCallTerminationReason
) provides details about why the call terminated. CallTerminationReason
fields:cause: String?
: A high-level cause string (e.g., "CALL_REJECTED", "USER_BUSY", "NORMAL_CLEARING").causeCode: Int?
: A numerical code associated with the cause (e.g., 21 for CALL_REJECTED, 17 for USER_BUSY, 16 for NORMAL_CLEARING).sipCode: Int?
: The SIP response code, if applicable (e.g., 403, 486, 404).sipReason: String?
: The SIP reason phrase, if applicable (e.g., "Forbidden", "Busy Here", "Not Found").
- The call has ended. The optional
CallState.ERROR
:- A general error occurred related to this specific call (e.g., failure to create offer/answer, media negotiation issues). This state itself does not carry a detailed reason object; specific error details might be logged internally or manifest as a transition to
CallState.DONE
with a reason.
- A general error occurred related to this specific call (e.g., failure to create offer/answer, media negotiation issues). This state itself does not carry a detailed reason object; specific error details might be logged internally or manifest as a transition to
3. Enriched ByeResponse
Details (via socketResponseLiveData
)
When a call is terminated by the remote party, a BYE
message is received. The TxSocketListener.onByeReceived(jsonObject: JsonObject)
method is triggered, and TelnyxClient
processes this.
- The
com.telnyx.webrtc.sdk.verto.receive.ByeResponse
object, which is delivered as theresult
withinReceivedMessageBody
(whenmethod
isSocketMethod.BYE.methodName
) viasocketResponseLiveData
, is now enriched. - It contains the same detailed termination fields as
CallTerminationReason
:callId
,cause
,causeCode
,sipCode
, andsipReason
.
4. Error/Cause Code Reference Table
This table provides common causes and codes. For a comprehensive list of SIP codes and detailed troubleshooting, always refer to the Telnyx Troubleshooting Guide for Call Completion.
Error Source / Category | errorMessage (Example) / cause (Example) | errorCode (from SocketResponse ) / causeCode (from CallTerminationReason ) | sipCode (from CallTerminationReason ) | sipReason (Example) | Description / Common Scenario |
---|---|---|---|---|---|
SocketResponse Errors | "Gateway registration has timed out" | SocketError.GATEWAY_TIMEOUT_ERROR.errorCode (-32003) | N/A | N/A | Gateway registration timed out. |
"Gateway registration has failed" | SocketError.GATEWAY_FAILURE_ERROR.errorCode (-32004) | N/A | N/A | Gateway registration failed after retries. | |
"Login Incorrect" | SocketError.CREDENTIAL_ERROR.errorCode (-32001) (if from server JSON) | N/A | N/A | Credential authentication error. | |
"Invalid Token" | SocketError.TOKEN_ERROR.errorCode (-32000) (if from server JSON) | N/A | N/A | Token authentication error. | |
"No Network Connection" | null | N/A | N/A | Client-side detection: no network on connect. | |
CallTerminationReason (from CallState.DONE or ByeResponse ) | |||||
General Call Clearing | NORMAL_CLEARING | 16 | N/A | N/A | Call ended normally. |
USER_BUSY | 17 | 486 | "Busy Here" | Called party is busy. | |
CALL_REJECTED | 21 | 403 | "Forbidden" | Call rejected (invalid caller ID, auth failure, etc.). | |
UNALLOCATED_NUMBER | 1 | 404 | "Not Found" | Dialed number does not exist. | |
NO_ANSWER | 19 | 480 | "Temporarily Unavailable" | Callee did not answer. | |
INCOMPATIBLE_DESTINATION | 88 | 488/606 | "Not Acceptable Here" | Media negotiation failure. | |
RECOVERY_ON_TIMER_EXPIRE | 102 | N/A (often 408) | "Request Timeout" | Necessary response not received in time. | |
SDK Internal Errors | AnswerError (example cause ) | N/A | N/A | "No SDP in answer response" | SDK specific error during call setup. |
Note: Not all fields (cause
, causeCode
, sipCode
, sipReason
, errorCode
) will be present for every error or DONE
state. Presence depends on the nature and source of the event.
More SIP codes and their meanings can be found in the Telnyx SIP Response Codes Guide.
Guide: Consuming Errors and Call Events
This section explains how to effectively use the error and state information provided by the SDK.
Observing socketResponseLiveData
(for General SDK Events & Errors)
TelnyxClient.socketResponseLiveData
is the primary channel for general SDK events, including connection status, errors (now with errorCode
), and messages like incoming BYE
.
// In your Activity or ViewModel
telnyxClient.socketResponseLiveData.observe(this, Observer { response ->
when (response.status) {
SocketStatus.ERROR -> {
Log.e("TelnyxSDK", "General SDK Error: ${response.errorMessage}, Code: ${response.errorCode}")
// Example: if (response.errorCode == -32003) { /* Handle Gateway Timeout */ }
// Handle gateway registration issues, WebSocket errors, no network on connect, etc.
}
SocketStatus.MESSAGERECEIVED -> {
response.data?.let { receivedMessageBody ->
if (receivedMessageBody.method == SocketMethod.BYE.methodName) {
val byeResponse = receivedMessageBody.result as? com.telnyx.webrtc.sdk.verto.receive.ByeResponse
byeResponse?.let {
val terminationMessage = "Remote party ended call (${it.callId}). " +
"Reason: ${it.cause ?: "N/A"} (${it.causeCode ?: "N/A"})" +
(it.sipCode?.let { sc -> " (SIP: $sc ${it.sipReason ?: ""})" } ?: "")
Log.i("TelnyxSDK_Bye", terminationMessage)
// The specific Call object's callStateFlow will also transition to CallState.DONE with this reason.
}
}
// Handle other methods like INVITE, ANSWER, LOGIN, CLIENT_READY etc.
}
}
// Handle other statuses: ESTABLISHED, LOADING, DISCONNECT
}
})
Observing Call.callStateFlow
(for Per-Call State and Reasons)
For each individual Call
object, observe its callStateFlow
to get detailed state transitions and associated reasons.
// Assuming 'myCall' is an active Call object
myCall.callStateFlow.collect { state ->
when (state) {
is CallState.ACTIVE -> {
Log.i("CallState", "Call ${myCall.callId} is ACTIVE")
// Update UI for active call
}
is CallState.DONE -> {
val reason = state.reason
val message = "Call ${myCall.callId} ENDED. " +
(reason?.let {
"Cause: ${it.cause ?: "Unknown"} (${it.causeCode ?: "N/A"}), " +
"SIP: ${it.sipCode ?: "N/A"} ${it.sipReason ?: ""}"
} ?: "No specific reason provided.")
Log.i("CallState_Done", message)
// Display termination reason to user, clean up call UI
}
is CallState.DROPPED -> {
Log.w("CallState", "Call ${myCall.callId} DROPPED. Reason: ${state.callNetworkChangeReason.description}")
// Inform user, potentially offer retry or end call UI
}
is CallState.RECONNECTING -> {
Log.i("CallState", "Call ${myCall.callId} RECONNECTING. Reason: ${state.callNetworkChangeReason.description}")
// Show reconnecting indicator
}
is CallState.ERROR -> {
Log.e("CallState", "Call ${myCall.callId} entered ERROR state.")
// Display error to user, clean up call UI
}
// Handle other states: NEW, CONNECTING, RINGING, HELD
else -> Log.d("CallState", "Call ${myCall.callId} is now ${state.javaClass.simpleName}")
}
}
Best Practices for Error and State Handling
- Observe Both Channels: Use
socketResponseLiveData
for global SDK status/errors (includingerrorCode
) andcall.callStateFlow
for individual call lifecycle management. - Log Extensively: During development, log error messages (with codes), call states, and reasons to aid in debugging.
- Provide Clear User Feedback: Translate technical error codes and states into user-understandable messages.
- Use
SocketResponse.errorCode
to distinguish specific socket/gateway errors. - For
CallState.DONE
with areason
, use thecause
,sipCode
, andsipReason
to provide specific feedback (e.g., "User Busy", "Invalid Number", "Call Rejected: Restricted Area"). Refer to the Telnyx Troubleshooting Guide for common interpretations. - For
CallState.DROPPED
orCallState.RECONNECTING
, inform the user about network issues.
- Use
- Implement Recovery/Retry Logic: For network-related drops or gateway issues (identified by specific error codes or call states), consider implementing reconnection attempts or prompting the user.
- Graceful Degradation: If critical errors occur (e.g., persistent gateway failure -
errorCode
-32004), ensure your app handles this gracefully, perhaps by disabling calling features and informing the user.
Additional Resources
- Telnyx WebRTC Android SDK GitHub Repository
- API Documentation
- Telnyx Troubleshooting Guide for Call Completion (Essential for interpreting SIP codes and call failure reasons)
- Telnyx SIP Response Codes Guide (For a detailed list of SIP codes)