Skip to main content

Documentation Index

Fetch the complete documentation index at: https://developers.telnyx.com/llms.txt

Use this file to discover all available pages before exploring further.

Error Handling

The SDK exposes error-related behavior through three main channels:
EventPurposeRecommended use
telnyx.errorFatal or blocking SDK errorsShow actionable errors, retry, re-authenticate
telnyx.warningNon-fatal quality, connectivity, and token warningsShow degraded-state UI, collect telemetry
telnyx.notificationCall lifecycle updates and compatibility notificationsDrive call UI and hangup handling
Use telnyx.ready to know when the client is authenticated and the gateway is ready. Do not treat readiness as a notification case.
Version note: The structured error and warning system (TELNYX_ERROR_CODES, telnyx.warning, TelnyxError) was introduced after v2.25.25. If you are on v2.25.25, see the Error handling in v2.25.25 section below.

Structured Errors (telnyx.error)

telnyx.error is the primary error surface. Listen for it to handle authentication failures, media errors, and connection issues.

Imports

import {
  SwEvent,
  TelnyxError,
  TELNYX_ERROR_CODES,
  isMediaRecoveryErrorEvent,
} from '@telnyx/webrtc';

Basic example

client.on(SwEvent.Error, (event) => {
  if (isMediaRecoveryErrorEvent(event)) {
    openPermissionsDialog({
      deadline: event.retryDeadline,
      onRetry: () => event.resume(),
      onCancel: () => event.reject(),
    });
    return;
  }

  if (!(event.error instanceof TelnyxError)) {
    showErrorMessage('An unknown SDK error occurred.');
    return;
  }

  switch (event.error.code) {
    case TELNYX_ERROR_CODES.NETWORK_OFFLINE:
      showErrorMessage('You appear to be offline.');
      break;
    case TELNYX_ERROR_CODES.AUTHENTICATION_REQUIRED:
      showErrorMessage('Session expired. Please authenticate again.');
      break;
    default:
      showErrorMessage(event.error.message);
  }
});

Media permission recovery

When mediaPermissionsRecovery.enabled is configured and getUserMedia() fails while answering a call, the error event includes recoverable: true with resume() and reject() callbacks:
const client = new TelnyxRTC({
  login_token: jwt,
  mediaPermissionsRecovery: {
    enabled: true,
    timeout: 10000,
  },
});

client.on(SwEvent.Error, (event) => {
  if (isMediaRecoveryErrorEvent(event)) {
    // Show a dialog asking the user to grant microphone permission
    // event.retryDeadline is the timestamp by which they must act
    showPermissionDialog({
      onGrant: () => event.resume(),
      onDismiss: () => event.reject(),
    });
  }
});

Error Code Reference

Each error below is classified as fatal or recoverable and includes guidance on what action you should take versus what the SDK handles automatically.

SDP errors

CodeNameFatal?Customer actionSDK behavior
40001SDP_CREATE_OFFER_FAILEDFatalShow error to user; retry with client.newCall()Call is not established
40002SDP_CREATE_ANSWER_FAILEDFatalShow error to user; the inbound call cannot be answeredCall is rejected
40003SDP_SET_LOCAL_DESCRIPTION_FAILEDFatalShow error to user; retry the callCall setup fails
40004SDP_SET_REMOTE_DESCRIPTION_FAILEDFatalShow error to user; retry the callCall setup fails
40005SDP_SEND_FAILEDFatalShow error to user; retry the callSignaling could not be sent

Media errors

CodeNameFatal?Customer actionSDK behavior
42001MEDIA_MICROPHONE_PERMISSION_DENIEDFatal unless mediaPermissionsRecovery is enabledPrompt user to grant microphone permission in browser/OS settingsCall fails; if mediaPermissionsRecovery.enabled, a recoverable error is emitted instead
42002MEDIA_DEVICE_NOT_FOUNDFatalCheck that a microphone is connected and the deviceId is validCall fails
42003MEDIA_GET_USER_MEDIA_FAILEDFatal unless mediaPermissionsRecovery is enabledCheck browser permissions and device availability; retryCall fails; if mediaPermissionsRecovery.enabled, a recoverable error is emitted instead

Call-control errors

CodeNameFatal?Customer actionSDK behavior
44001HOLD_FAILEDNon-fatal per callRetry hold operationHold is not applied
44002INVALID_CALL_PARAMETERSFatalFix call parameters before retryingCall is not established
44003BYE_SEND_FAILEDNon-fatalCall is still hung up locally; no action neededLocal hangup completes but BYE signal may not reach the server
44004SUBSCRIBE_FAILEDFatalCheck connection state; may need to reconnectCannot subscribe to call events
44005PEER_CLOSED_DURING_INITFatalRetry the callPeer connection closed before call setup completed

WebSocket and transport errors

CodeNameFatal?Customer actionSDK behavior
45001WEBSOCKET_CONNECTION_FAILEDFatal for sessionCheck network connectivity; call client.connect() to retrySession is not established
45002WEBSOCKET_ERRORFatal for sessionShow reconnecting UI; SDK auto-reconnects by default (autoReconnect is enabled by default). If you disabled autoReconnect, call client.connect() manually. Wait for telnyx.ready to confirm recoveryWebSocket error occurred; SDK schedules connect() after reconnectDelay when autoReconnect is not disabled
45003RECONNECTION_EXHAUSTEDFatal for sessionAll automatic reconnect attempts exhausted. Call client.disconnect() then client.connect() to start a fresh connection, or recreate the client instanceAll automatic reconnect attempts have been used
45004GATEWAY_FAILEDFatal for sessionShow reconnecting UI; SDK auto-reconnects by default (autoReconnect is enabled by default). If you disabled autoReconnect, call client.connect() manually. Wait for telnyx.ready to confirm recoveryGateway reported FAILED or FAIL_WAIT; SDK continues retrying when autoReconnect is not disabled
autoReconnect is enabled by default. Unless you explicitly set autoReconnect: false, the SDK handles reconnection automatically for WEBSOCKET_ERROR and GATEWAY_FAILED. You only need to call client.connect() manually if you disabled autoReconnect or after RECONNECTION_EXHAUSTED.

Authentication and session errors

CodeNameFatal?Customer actionSDK behavior
46001LOGIN_FAILEDFatal for sessionRe-authenticate using client.login() without recreating the instanceRegistration never reached ready state
46002INVALID_CREDENTIALSFatal for sessionFix credential parameters; re-authenticate using client.login()Login was rejected before request was sent
46003AUTHENTICATION_REQUIREDFatal for sessionRe-authenticate using client.login({ creds: { login_token: newToken } }) without recreating the instanceRequest was sent before auth completed or after auth was lost
48001NETWORK_OFFLINEFatal for sessionRestore network connectivity; SDK auto-reconnects when back onlineBrowser offline event fired
49001UNEXPECTED_ERRORFatalCheck logs for details; retry the operationUnclassified failure during peer/call setup
Re-authenticate without recreating the instance. For LOGIN_FAILED (46001), INVALID_CREDENTIALS (46002), and AUTHENTICATION_REQUIRED (46003), use client.login() to re-authenticate on the existing connection:
// Refresh token without recreating TelnyxRTC
await client.login({ creds: { login_token: newToken } });

Structured Warnings (telnyx.warning)

Warnings are never fatal. They describe degraded behavior, quality issues, or situations that may need user action before the session breaks. The SDK continues operating after emitting a warning.

Basic example

import { SwEvent, TELNYX_WARNING_CODES } from '@telnyx/webrtc';

client.on(SwEvent.Warning, ({ warning, callId }) => {
  if (warning.code === TELNYX_WARNING_CODES.TOKEN_EXPIRING_SOON) {
    refreshToken();
    return;
  }

  if (warning.code === TELNYX_WARNING_CODES.PEER_CONNECTION_FAILED) {
    showWarningBanner('Call is reconnecting');
    return;
  }

  console.warn(`[${warning.code}] ${warning.name}: ${warning.message}`);
});

Warning code reference

Network quality warnings

CodeNameAuto-recovered?Customer action
31001HIGH_RTTMay self-resolveShow quality indicator; no immediate action needed
31002HIGH_JITTERMay self-resolveShow quality indicator; no immediate action needed
31003HIGH_PACKET_LOSSMay self-resolveShow quality indicator; no immediate action needed
31004LOW_MOSMay self-resolveShow quality indicator; consider advising user

Data-flow warnings

CodeNameAuto-recovered?Customer action
32001LOW_BYTES_RECEIVEDMay self-resolve on reconnectCheck remote party; show degraded audio indicator
32002LOW_BYTES_SENTMay self-resolve on reconnectCheck local microphone; show degraded audio indicator

Connectivity warnings

CodeNameAuto-recovered?Customer action
33001ICE_CONNECTIVITY_LOSTSDK attempts ICE reconnectShow reconnecting indicator; wait for recovery or PEER_CONNECTION_FAILED
33002ICE_GATHERING_TIMEOUTMay self-resolveCheck firewall/STUN/TURN config; show warning
33003ICE_GATHERING_EMPTYNoCheck network/firewall settings; STUN/TURN may be blocked
33004PEER_CONNECTION_FAILEDMay recover — SDK may attempt ICE restartShow reconnecting/degraded UI; wait for SDK recovery; only clean up after final hangup or call termination
33005ONLY_HOST_ICE_CANDIDATESNoCheck STUN/TURN config; call may work on local network only
33006ANSWER_WHILE_PEER_ACTIVENoEnsure answer() is called only once per call; disable the answer button after the first click; check that answer() is not invoked from multiple event handlers

Authentication and session warnings

CodeNameAuto-recovered?Customer action
34001TOKEN_EXPIRING_SOONNo, but preventableRefresh the token before it expires; you have ~120 seconds
35001SESSION_NOT_REATTACHEDNoActive calls were lost after reconnect; clean up call UI

Call Termination Data

When a call reaches hangup, inspect these fields on the Call object:
FieldTypeMeaning
causestring | nullHigh-level cause (USER_BUSY, CALL_REJECTED, etc.)
causeCodenumber | nullNumeric cause code
sipCodenumber | nullSIP response code when available
sipReasonstring | nullSIP reason phrase when available
Common causes:
CauseMeaning
NORMAL_CLEARINGExpected call completion
USER_BUSYRemote party was busy
CALL_REJECTEDRemote party rejected the call
NO_ANSWERCall timed out unanswered
UNALLOCATED_NUMBERDialed number is invalid or does not exist

Socket Events

telnyx.socket.close

Delivers the browser CloseEvent. During a forced safety cleanup, the SDK emits a synthetic abnormal close with code: 1006 and wasClean: false. Useful close codes:
CodeMeaning
1000Normal closure
1001Going away
1006Abnormal closure
1011Internal error

telnyx.socket.error

Delivers { error: ErrorEvent, sessionId: string }. Browsers expose very little information for WebSocket errors. The SDK also emits telnyx.error with code 45002 (WEBSOCKET_ERROR) when ws.onerror fires.

Connection State Helpers

The browser session exposes WebSocket state helpers on client.connection:
GetterMeaning
client.connection.connectedWebSocket is in OPEN
client.connection.isAliveCONNECTING or OPEN
client.connection.isDeadCLOSING or CLOSED
Example:
const placeCall = (destinationNumber) => {
  if (!client.connection.connected) {
    showErrorMessage('Still connecting to Telnyx. Please try again shortly.');
    return;
  }

  client.newCall({ destinationNumber });
};

Reconnection Behavior

On telnyx.socket.close or telnyx.socket.error, the SDK clears subscriptions and resets gateway readiness state. autoReconnect is enabled by default; unless you set autoReconnect: false, the SDK automatically schedules connect() after reconnectDelay (1000ms).

Gateway retry behavior

  • UNREGED / NOREG: Up to 5 registration retries, each delayed 2-6 seconds randomly. After that, LOGIN_FAILED (46001).
  • FAILED / FAIL_WAIT: GATEWAY_FAILED (45004) emitted on first detection. Up to 5 retries with 2-6 second random delay before RECONNECTION_EXHAUSTED (45003).

Keeping media alive

If keepConnectionAliveOnSocketClose is true, the SDK preserves active peer connections while signaling reconnects. Recovery can create a new Call object with recoveredCallId.

Clearing reconnect stickiness

By default, the SDK reconnects to the same b2bua-rtc instance. To break this stickiness and route to a different instance:
// Before reconnecting
client.clearReconnectToken();

// Or configure the SDK to skip the last voice SDK ID on reconnect
const client = new TelnyxRTC({
  login_token: jwt,
  skipLastVoiceSdkId: true,
});
Note: clearReconnectToken() and skipLastVoiceSdkId are available in @telnyx/webrtc@2.26.4.

Error Handling in v2.25.25

Important: If you are using SDK version 2.25.25, the error handling architecture is fundamentally different from the current version. This section documents the v2.25.25 error surface.

What is different in v2.25.25

Featurev2.25.25v2.26.0+
Structured error codesNot availableTELNYX_ERROR_CODES with numeric codes
telnyx.warning eventNot availableAvailable with TELNYX_WARNING_CODES
TelnyxError classNot availableStructured error class with .code, .name, .message
isMediaRecoveryErrorEvent()Not availableAvailable for media permission recovery
SDK_ERRORS / SDK_WARNINGSNot availableAvailable for error/warning metadata
Primary error surfacetelnyx.error (raw Error) + telnyx.notificationtelnyx.error (structured) + telnyx.warning

Error events in v2.25.25

In v2.25.25, errors are emitted through telnyx.error and telnyx.notification: telnyx.error — Session-level errors with raw Error objects (no .code property):
client.on(SwEvent.Error, (event) => {
  // event.error is a plain Error object — no structured code
  // event.type may include ERROR_TYPE.invalidCredentialsOptions
  // event.sessionId is available
  console.error('SDK error:', event.error?.message || event.error);
});
telnyx.notification — Carries both call lifecycle updates and error information. This is the only recommended way to handle media, peer connection, and signaling errors in v2.25.25. Do not listen for telnyx.rtc.mediaError, telnyx.rtc.peerConnectionFailureError, or telnyx.rtc.peerConnectionSignalingStateClosed directly — those are internal events. Use telnyx.notification instead:
client.on(SwEvent.Notification, (notification) => {
  switch (notification.type) {
    case 'userMediaError':
      // notification.error — raw browser Error/DOMException
      // notification.errorName — error name string
      // notification.errorMessage — error message string
      // notification.call — the Call object (if available)
      // SDK automatically hangs up the call
      showPermissionPrompt(notification.errorMessage);
      break;

    case 'peerConnectionFailureError':
      // notification.error — raw error
      // Peer connection failed, but the call may be recovered by the server
      // via attach with 'recovering' state. Show degraded UI and wait.
      showReconnectingBanner();
      break;

    case 'signalingStateClosed':
      // Peer signaling state closed — peer is not recoverable
      // But the call may still recover through server attach
      // Only clean up after a final hangup/callUpdate confirms loss
      showReconnectingBanner();
      break;

    case 'callUpdate':
      // Normal call lifecycle
      handleCallUpdate(notification.call);
      break;
  }
});
Error-related notification types:
notification.typeMeaningFatal?Customer action
userMediaErrorMedia device access failedYes (for the call)Prompt user for microphone permission; the SDK hangs up the call automatically
peerConnectionFailureErrorPeer connection failedPeer connection not recoverable, but call may be recoveredShow reconnecting/degraded UI; the call may be restored automatically by the server via attach with recovering state; only clean up after a final hangup/call update confirms loss
signalingStateClosedPeer signaling state closedPeer connection not recoverable, but call may still be recovered by serverShow reconnecting/degraded UI; the call may recover through auto-created recovering call with the same call_id; only clean up after a final hangup/call update confirms loss

Authentication errors in v2.25.25

Login errors are emitted on telnyx.error with a type field for invalid credentials. You can re-authenticate using client.login() without recreating the TelnyxRTC instance:
import { SwEvent, ERROR_TYPE } from '@telnyx/webrtc';

client.on(SwEvent.Error, (event) => {
  if (event.type === ERROR_TYPE.invalidCredentialsOptions) {
    // Credentials were invalid before the request was sent
    showLoginError('Please check your credentials.');
    return;
  }

  // For LOGIN_FAILED or AUTHENTICATION_REQUIRED, re-login without recreating the instance:
  // await client.login({ creds: { login_token: newToken } });

  // Other errors — generic handling
  showErrorMessage(event.error?.message || 'An error occurred');
});

Reconnection in v2.25.25

Reconnection behavior is the same as the current version with these differences:
  • autoReconnect is enabled by default; the SDK automatically reconnects unless you set autoReconnect: false
  • No maxReconnectAttempts option (unlimited reconnects; note that this option also does not exist in the latest version)
  • No clearReconnectToken() method
  • No skipLastVoiceSdkId option
  • keepConnectionAliveOnSocketClose is available

Migrating from v2.25.25 to the latest

If you are upgrading from v2.25.25 to the latest version:
  1. Replace telnyx.notification error handling — use telnyx.error for fatal errors and telnyx.warning for non-fatal conditions. Keep telnyx.notification for call lifecycle only.
  2. Replace notification.type === 'userMediaError' handling with telnyx.error listener switching on event.error.code (42001, 42002, 42003).
  3. Replace notification.type === 'peerConnectionFailureError' handling with telnyx.warning listener for PEER_CONNECTION_FAILED (33004).
  4. Replace notification.type === 'signalingStateClosed' handling with telnyx.warning listener for the appropriate warning code.
  5. Replace ERROR_TYPE.invalidCredentialsOptions checks with event.error.code === TELNYX_ERROR_CODES.INVALID_CREDENTIALS (46002). Use client.login() to re-authenticate without recreating the TelnyxRTC instance.
  6. Import new symbols: TelnyxError, TELNYX_ERROR_CODES, TELNYX_WARNING_CODES, isMediaRecoveryErrorEvent.
  7. If you need media permission recovery for inbound calls, enable mediaPermissionsRecovery and handle isMediaRecoveryErrorEvent(event).
  8. Treat telnyx.ready as the only readiness signal. The vertoClientReady notification type is no longer emitted on telnyx.notification.
The legacy RTC events (telnyx.rtc.mediaError, telnyx.rtc.peerConnectionFailureError, telnyx.rtc.peerConnectionSignalingStateClosed) are still emitted for backward compatibility but should not be used for new integrations.

See Also