Error Handling
Handle errors gracefully and provide a good user experience when things go wrong.
Common Errors
| Error | Cause | Solution |
|---|---|---|
"Establishing connection to servers" |
PTT called while reconnecting | Retry after connection_change event shows connected |
"TalkerClient requires a userAuthToken" |
Missing constructor parameter | Pass all required credentials from backend |
"Failed to get credentials" |
Invalid SDK key or network error | Check SDK key and network connectivity |
"WebRTC data channel failed to open" |
Connection timeout | Check network, retry connection |
PTT Error Handling
The startTalking() method returns a result object instead of throwing errors:
TypeScript
async function handlePTT(channelId: string) {
const result = await talker.startTalking(channelId);
if (result.success) {
// PTT started successfully
updateUI('talking');
return;
}
// Handle different error codes
switch (result.code) {
case 'not_connected':
// SDK will auto-reconnect
showToast('Connecting to server...');
waitForConnection();
break;
case 'no_user_id':
// Developer error - missing userId
console.error('Configuration error:', result.message);
break;
case 'already_talking':
// Not really an error - already in PTT session
break;
case 'channel_busy':
// Someone else is talking on this channel
showToast('Channel is busy. Please wait for the current speaker to finish.');
break;
case 'error':
// Show error to user
showError(result.message);
break;
}
}
Connection Error Recovery
TypeScript
class ConnectionManager {
private pendingAction: (() => Promise) | null = null;
constructor(private talker: TalkerClient) {
talker.on('connection_change', ({ connected }) => {
if (connected && this.pendingAction) {
this.pendingAction();
this.pendingAction = null;
}
});
}
async startTalkingWithRetry(channelId: string) {
const result = await this.talker.startTalking(channelId);
if (!result.success && result.code === 'not_connected') {
// Queue the action to retry when connected
this.pendingAction = () => this.talker.startTalking(channelId);
showUI('connecting');
}
}
}
Microphone Permission Errors
TypeScript
async function requestMicrophoneAccess(): Promise {
try {
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
// Success - stop the stream
stream.getTracks().forEach(track => track.stop());
return true;
} catch (error) {
handleMicrophoneError(error);
return false;
}
}
function handleMicrophoneError(error: Error) {
switch (error.name) {
case 'NotAllowedError':
showError(
'Microphone access denied. Please allow microphone access in your browser settings.',
{ showSettingsButton: true }
);
break;
case 'NotFoundError':
showError(
'No microphone found. Please connect a microphone and try again.'
);
break;
case 'NotReadableError':
showError(
'Microphone is in use by another application. Please close other apps using the microphone.'
);
break;
case 'OverconstrainedError':
showError(
'Could not find a suitable microphone. Please try a different device.'
);
break;
default:
showError(`Microphone error: ${error.message}`);
}
}
Network Error Handling
TypeScript
// Monitor online/offline status
window.addEventListener('online', () => {
showToast('Back online');
talker.reconnect();
});
window.addEventListener('offline', () => {
showToast('You are offline. PTT will resume when connected.');
});
// Check before important operations
async function performAction() {
if (!navigator.onLine) {
showError('No internet connection. Please check your network.');
return;
}
// Proceed with action...
}
API Error Handling
TypeScript
async function createChannel(name: string, participants: string[]) {
try {
const channel = await talker.createChannel({
name,
participants,
workspaceId: 'workspace-id',
});
return channel;
} catch (error) {
if (error.response) {
// Server responded with error
switch (error.response.status) {
case 400:
showError('Invalid channel configuration');
break;
case 401:
showError('Session expired. Please log in again.');
handleLogout();
break;
case 403:
showError('You do not have permission to create channels');
break;
case 429:
showError('Too many requests. Please wait and try again.');
break;
default:
showError('Server error. Please try again later.');
}
} else if (error.request) {
// Network error
showError('Network error. Please check your connection.');
} else {
// Other error
showError('An error occurred: ' + error.message);
}
throw error;
}
}
Global Error Handler
TypeScript
class TalkerErrorHandler {
private errorCallbacks: ((error: Error) => void)[] = [];
onError(callback: (error: Error) => void) {
this.errorCallbacks.push(callback);
return () => {
this.errorCallbacks = this.errorCallbacks.filter(cb => cb !== callback);
};
}
handleError(error: Error, context?: string) {
console.error(`[Talker Error${context ? ` - ${context}` : ''}]:`, error);
// Log to error tracking service
this.logToService(error, context);
// Notify all listeners
this.errorCallbacks.forEach(cb => cb(error));
}
private logToService(error: Error, context?: string) {
// Send to Sentry, LogRocket, etc.
if (typeof Sentry !== 'undefined') {
Sentry.captureException(error, {
extra: { context },
});
}
}
}
// Usage
const errorHandler = new TalkerErrorHandler();
errorHandler.onError((error) => {
showToast(`Error: ${error.message}`, 'error');
});
User-Friendly Error Messages
TypeScript
const ERROR_MESSAGES: Record = {
// Connection errors
'not_connected': 'Connecting to server. Please wait...',
'connection_timeout': 'Connection timed out. Retrying...',
'connection_failed': 'Could not connect to server. Please check your internet.',
// PTT errors
'no_user_id': 'User not authenticated. Please refresh the page.',
'already_talking': 'Already in a call.',
'channel_busy': 'Channel is busy. Someone else is talking.',
'microphone_denied': 'Microphone access denied. Please enable in settings.',
// General errors
'network_error': 'Network error. Please check your connection.',
'server_error': 'Server error. Please try again later.',
'unknown_error': 'Something went wrong. Please try again.',
};
function getErrorMessage(code: string, fallback?: string): string {
return ERROR_MESSAGES[code] || fallback || ERROR_MESSAGES['unknown_error'];
}
// Usage
const result = await talker.startTalking(channelId);
if (!result.success) {
showToast(getErrorMessage(result.code, result.message));
}
Error UI Component
React Component
interface ErrorBannerProps {
error: string | null;
onRetry?: () => void;
onDismiss?: () => void;
}
function ErrorBanner({ error, onRetry, onDismiss }: ErrorBannerProps) {
if (!error) return null;
return (
<div className="error-banner">
<span className="error-icon">⚠️</span>
<span className="error-message">{error}</span>
<div className="error-actions">
{onRetry && (
<button onClick={onRetry}>Retry</button>
)}
{onDismiss && (
<button onClick={onDismiss}>Dismiss</button>
)}
</div>
</div>
);
}
Best Practices
- Always provide actionable error messages
- Include retry options when appropriate
- Log errors for debugging but show user-friendly messages
- Handle offline scenarios gracefully