WebRTC JS SDK Error Handling
This guide reflects the current SDK surface in this branch. The current SDK exposes error-related behavior through three main channels:telnyx.errorfor structured failures.telnyx.warningfor degraded but recoverable conditions.telnyx.notificationfor call/session updates plus a small set of backward-compatible notifications.
telnyx.ready to know when the client is authenticated and the gateway is ready. Do not treat readiness as a telnyx.notification case.
Table of Contents
- Event Overview
- Structured Errors (
telnyx.error) - Structured Warnings (
telnyx.warning) - Notifications (
telnyx.notification) - Call Termination Data
- Socket Events
- Reconnection Behavior
- Legacy RTC Events and Migration
Event Overview
| Event | Purpose | Recommended use |
|---|---|---|
telnyx.ready | Client is authenticated and gateway reached REGISTER or REGED | Enable calling UI and flush any reconnect state |
telnyx.error | Fatal or blocking SDK errors | Show actionable errors, retry, re-authenticate, or fail the current action |
telnyx.warning | Non-fatal quality, connectivity, and token warnings | Show degraded-state UI and collect telemetry |
telnyx.notification | Call lifecycle updates and compatibility notifications | Drive call UI and hangup handling |
telnyx.socket.close | Raw WebSocket close event | Log close codes and monitor reconnect behavior |
telnyx.socket.error | Raw WebSocket error wrapper | Log opaque socket failures alongside sessionId |
Structured Errors (telnyx.error)
telnyx.error is the primary error surface in the current SDK.
Imports
TelnyxErroris the structured error class emitted by the SDK.isMediaRecoveryErrorEvent()is the type guard for inbound permission recovery flows.TELNYX_ERROR_CODESprovides named constants for numeric error comparisons.SDK_ERRORSprovides the registered error metadata.
Payload shapes
recoverable: true is used only for inbound media-permission recovery when mediaPermissionsRecovery.enabled is configured and the initial getUserMedia() attempt fails while answering a call.
Basic example
Error code reference
SDP errors
| Code | Name | Message | Typical trigger |
|---|---|---|---|
40001 | SDP_CREATE_OFFER_FAILED | Failed to create call offer | RTCPeerConnection.createOffer() failed |
40002 | SDP_CREATE_ANSWER_FAILED | Failed to answer the call | RTCPeerConnection.createAnswer() failed |
40003 | SDP_SET_LOCAL_DESCRIPTION_FAILED | Failed to apply local call settings | setLocalDescription() failed |
40004 | SDP_SET_REMOTE_DESCRIPTION_FAILED | Failed to apply remote call settings | setRemoteDescription() failed |
40005 | SDP_SEND_FAILED | Failed to send call data to server | Invite/answer signaling could not be sent |
Media errors
| Code | Name | Message | Typical trigger |
|---|---|---|---|
42001 | MEDIA_MICROPHONE_PERMISSION_DENIED | Microphone access denied | Browser or OS denied microphone permission |
42002 | MEDIA_DEVICE_NOT_FOUND | No microphone found | Missing/disconnected device or invalid deviceId |
42003 | MEDIA_GET_USER_MEDIA_FAILED | Failed to access microphone | getUserMedia() failed for another reason |
Call-control errors
| Code | Name | Message | Typical trigger |
|---|---|---|---|
44001 | HOLD_FAILED | Failed to hold the call | Hold request failed |
44002 | INVALID_CALL_PARAMETERS | Invalid call parameters | Required call params were missing or invalid |
44003 | BYE_SEND_FAILED | Failed to hang up cleanly | Local hangup succeeded but BYE could not be sent |
44004 | SUBSCRIBE_FAILED | Failed to subscribe to call events | Verto subscribe failed |
WebSocket and transport errors
| Code | Name | Message | Typical trigger |
|---|---|---|---|
45001 | WEBSOCKET_CONNECTION_FAILED | Unable to connect to server | WebSocket construction/connection failed |
45002 | WEBSOCKET_ERROR | Connection to server lost | Browser ws.onerror fired |
45003 | RECONNECTION_EXHAUSTED | Unable to reconnect to server | Gateway reconnect attempts were exhausted |
45004 | GATEWAY_FAILED | Gateway connection failed | Gateway reported FAILED or FAIL_WAIT |
Authentication and session errors
| Code | Name | Message | Typical trigger |
|---|---|---|---|
46001 | LOGIN_FAILED | Authentication failed | Login rejected or registration never reached ready state |
46002 | INVALID_CREDENTIALS | Invalid credential parameters | Client-side login validation failed before request send |
46003 | AUTHENTICATION_REQUIRED | Authentication required | Request sent before auth completed or after auth was lost |
48001 | NETWORK_OFFLINE | Device is offline | Browser offline event fired |
49001 | UNEXPECTED_ERROR | An unexpected error occurred | Unclassified failure during peer/call setup |
[!NOTE] Invalid login options currently also includetype: ERROR_TYPE.invalidCredentialsOptionson the runtime event payload for backward compatibility. The stable signal to key on is stillevent.error.code === TELNYX_ERROR_CODES.INVALID_CREDENTIALS.
Structured Warnings (telnyx.warning)
Warnings are not fatal. They describe degraded behavior, quality issues, or situations that may need user action before the session breaks.
Payload shape
Basic example
Warning code reference
Network quality warnings
| Code | Name | Message | Typical trigger |
|---|---|---|---|
31001 | HIGH_RTT | High network latency detected | RTT stayed above threshold |
31002 | HIGH_JITTER | High jitter detected | Jitter stayed above threshold |
31003 | HIGH_PACKET_LOSS | High packet loss detected | Packet loss stayed above threshold |
31004 | LOW_MOS | Low call quality score | MOS stayed below threshold |
Data-flow warnings
| Code | Name | Message | Typical trigger |
|---|---|---|---|
32001 | LOW_BYTES_RECEIVED | No audio data received | Remote audio bytes stopped increasing |
32002 | LOW_BYTES_SENT | No audio data being sent | Local audio bytes stopped increasing |
Connectivity warnings
| Code | Name | Message | Typical trigger |
|---|---|---|---|
33001 | ICE_CONNECTIVITY_LOST | Connection interrupted | ICE connection state became disconnected |
33002 | ICE_GATHERING_TIMEOUT | ICE gathering timed out | ICE gathering safety timeout fired |
33003 | ICE_GATHERING_EMPTY | No ICE candidates gathered | No candidates were collected |
33004 | PEER_CONNECTION_FAILED | Connection failed | Peer connection state became failed |
33005 | ONLY_HOST_ICE_CANDIDATES | Only local network candidates available | SDP contained only host ICE candidates |
Authentication and session warnings
| Code | Name | Message | Typical trigger |
|---|---|---|---|
34001 | TOKEN_EXPIRING_SOON | Authentication token expiring soon | JWT expires within 120 seconds |
35001 | SESSION_NOT_REATTACHED | Active call lost after reconnect | Server returned an empty reattached_sessions list while calls still existed locally |
Notifications (telnyx.notification)
telnyx.notification is still important, but it is no longer the main place to integrate error handling logic. Use it primarily for call lifecycle and UI synchronization.
Important clarification
telnyx.readyis separate fromtelnyx.notification.- The
vertoClientReadypayload is emitted ontelnyx.ready, not ontelnyx.notification.
Error-related notification types
notification.type | Current status | Payload notes |
|---|---|---|
callUpdate | Active | Includes call; use this for call state and hangup data |
userMediaError | Compatibility notification | Final media-failure notification. Includes raw browser/media error, errorName, errorMessage, and call when available |
peerConnectionFailureError | Compatibility notification | Includes raw error; structured replacement is warning 33004 |
signalingStateClosed | Active | Includes sessionId; indicates the current peer signaling state closed |
Listener scoping
Notifications are dispatched to the call scope first. If a call-level listener handles the notification, the session-level listener does not also receive it — it is one or the other, not both.onNotificationon a call (passed via call options) is the highest-priority hook for that call.client.on(SwEvent.Notification, ...)is the session-level fallback that only fires when no call-level listener is registered.- If no listeners exist at either level, the notification is silently dropped.
peerConnectionFailureError and signalingStateClosed, prefer call-scoped handling or the structured warning/error events when you need explicit call correlation. The compatibility notification payload itself does not include callId.
Screen-share behavior
Alltelnyx.notification dispatches are suppressed for calls created with screenShare: true.
If you need recovery state for a screen-share call, inspect the call object directly:
Example: hangup and recovery-aware call updates
Call Termination Data
When a call reacheshangup, inspect these fields on the Call object:
| Field | Type | Meaning |
|---|---|---|
cause | string | High-level cause such as USER_BUSY or CALL_REJECTED |
causeCode | number | Numeric cause code |
sipCode | number | SIP response code when available |
sipReason | string | SIP reason phrase when available |
| Field value | Meaning |
|---|---|
cause === 'NORMAL_CLEARING' | Expected call completion |
cause === 'USER_BUSY' | Remote party was busy |
cause === 'CALL_REJECTED' | Remote party rejected the call |
cause === 'NO_ANSWER' | Call timed out unanswered |
cause === 'UNALLOCATED_NUMBER' | Dialed number is invalid or does not exist |
cause === 'PURGE' | Call was purged from the system |
sipCode === 403 | Forbidden |
sipCode === 404 | Destination not found |
sipCode === 486 | Busy Here |
Socket Events
The SDK exposes both raw socket events and structured transport errors.telnyx.socket.close
telnyx.socket.close delivers the browser CloseEvent unchanged. During a forced safety cleanup, the SDK emits a synthetic abnormal close with:
code: 1006reason: 'STUCK_WS_TIMEOUT: Socket got stuck in CLOSING state and was forcefully cleaned up by safety timeout'wasClean: false
| Code | Meaning |
|---|---|
1000 | Normal closure |
1001 | Going away |
1002 | Protocol error |
1003 | Unsupported data |
1005 | No status code received |
1006 | Abnormal closure |
1011 | Internal error |
telnyx.socket.error
telnyx.socket.error delivers:
telnyx.error with code 45002 (WEBSOCKET_ERROR) when ws.onerror fires.
Connection state helpers
The browser session exposes WebSocket state helpers onclient.connection:
| Getter | Meaning |
|---|---|
client.connection.connecting | WebSocket is in CONNECTING |
client.connection.connected | WebSocket is in OPEN |
client.connection.closing | WebSocket is in CLOSING |
client.connection.closed | WebSocket is in CLOSED |
client.connection.isAlive | CONNECTING or OPEN |
client.connection.isDead | CLOSING or CLOSED |
Reconnection Behavior
The previous version of this document described a generic exponential-backoff flow. That is not how the current browser SDK reconnects.What the current SDK does
- On
telnyx.socket.closeortelnyx.socket.error, the SDK clears subscriptions and resets gateway readiness state. - If
autoReconnectis enabled, the browser session schedulesconnect()afterclient.reconnectDelay. - In the browser session,
reconnectDelayis currently1000ms. - When the gateway reports
REGISTERorREGEDagain, the SDK emitstelnyx.readyagain.
Gateway retry behavior
Gateway-state retries use separate jittered delays:UNREGED/NOREG: up to 5 registration retries, each delayed by a random2to6seconds. After that the SDK emitsLOGIN_FAILED(46001).FAILED/FAIL_WAIT:GATEWAY_FAILED(45004) is emitted on first detection. IfautoReconnectstays enabled, the SDK retries up to 5 times with a random2to6second delay beforeRECONNECTION_EXHAUSTED(45003).
Keeping media alive across socket loss
IfkeepConnectionAliveOnSocketClose is true, the SDK will try to preserve active peer connections while signaling reconnects.
- If the peer is still recoverable, the SDK disconnects and reconnects the socket while keeping the call alive.
- If the peer signaling state is already closed, the SDK falls back to a full reconnect path.
Recovery and call objects
Recovery can create a newCall object. When that happens:
- The new call exposes
recoveredCallId. - The call may stay in
recoveringuntil media/signaling are restored. - Your UI should remove or merge the old call UI using
recoveredCallId.
Legacy RTC Events and Migration
The SDK still exposes low-level RTC events, but new integrations should prefertelnyx.error, telnyx.warning, and telnyx.notification.
Legacy or low-level RTC events
| Event | Status | Preferred surface |
|---|---|---|
telnyx.rtc.mediaError | Legacy/compatibility | telnyx.error with 42001, 42002, or 42003 |
telnyx.rtc.peerConnectionFailureError | Legacy/compatibility | telnyx.warning with 33004 |
telnyx.rtc.peerConnectionSignalingStateClosed | Low-level active event | notification.type === 'signalingStateClosed' if you want the higher-level notification |
Migration checklist
- Add a
telnyx.errorlistener and switch onevent.error.code. - Add a
telnyx.warninglistener and switch onwarning.code. - Keep
telnyx.notificationforcallUpdateand any compatibility notifications you still depend on. - Treat
telnyx.readyas the only readiness signal. - Prefer
TELNYX_ERROR_CODESandTELNYX_WARNING_CODESover hard-coded numeric literals. - If you support inbound permission recovery, enable
mediaPermissionsRecoveryand handleisMediaRecoveryErrorEvent(event).