Comprehensive error handling for the ElevenLabs SDK with specific error classes for each HTTP status code.
/**
* Base error class for all ElevenLabs errors
*/
class ElevenLabsError extends Error {
statusCode?: number;
body?: unknown;
rawResponse?: Response;
}
/**
* Timeout error when request exceeds configured timeout
*/
class ElevenLabsTimeoutError extends Error {}Base error classes can be imported directly:
import {
ElevenLabsError,
ElevenLabsTimeoutError,
} from "@elevenlabs/elevenlabs-js";HTTP-specific error classes are accessed via the ElevenLabs namespace:
import { ElevenLabs } from "@elevenlabs/elevenlabs-js";
// Available error classes:
// ElevenLabs.BadRequestError, ElevenLabs.UnauthorizedError,
// ElevenLabs.ForbiddenError, ElevenLabs.NotFoundError,
// ElevenLabs.ConflictError, ElevenLabs.UnprocessableEntityError,
// ElevenLabs.TooEarlyError/**
* HTTP 400 - Bad Request
*/
class BadRequestError extends ElevenLabsError {
statusCode: 400;
}
/**
* HTTP 401 - Unauthorized (invalid or missing API key)
*/
class UnauthorizedError extends ElevenLabsError {
statusCode: 401;
}
/**
* HTTP 403 - Forbidden (insufficient permissions)
*/
class ForbiddenError extends ElevenLabsError {
statusCode: 403;
}
/**
* HTTP 404 - Not Found (resource doesn't exist)
*/
class NotFoundError extends ElevenLabsError {
statusCode: 404;
}
/**
* HTTP 409 - Conflict (resource conflict)
*/
class ConflictError extends ElevenLabsError {
statusCode: 409;
}
/**
* HTTP 422 - Unprocessable Entity (validation failed, most common)
*/
class UnprocessableEntityError extends ElevenLabsError {
statusCode: 422;
}
/**
* HTTP 425 - Too Early (request sent too early)
*/
class TooEarlyError extends ElevenLabsError {
statusCode: 425;
}import { ElevenLabsClient, ElevenLabsError, ElevenLabsTimeoutError } from "@elevenlabs/elevenlabs-js";
const client = new ElevenLabsClient({ apiKey: "your-api-key" });
try {
const audio = await client.textToSpeech.convert(voiceId, { text: "Hello" });
} catch (error) {
if (error instanceof ElevenLabsError) {
console.error(`API Error ${error.statusCode}:`, error.body);
} else if (error instanceof ElevenLabsTimeoutError) {
console.error('Request timed out');
} else {
console.error('Unknown error:', error);
}
}import { ElevenLabs, ElevenLabsTimeoutError } from "@elevenlabs/elevenlabs-js";
try {
const audio = await client.textToSpeech.convert(voiceId, { text: "Hello" });
} catch (error) {
if (error instanceof ElevenLabs.UnauthorizedError) {
console.error('Invalid API key - check your credentials');
} else if (error instanceof ElevenLabs.UnprocessableEntityError) {
console.error('Validation failed:', error.body);
} else if (error instanceof ElevenLabs.NotFoundError) {
console.error('Voice not found - check voice ID');
} else if (error instanceof ElevenLabs.ForbiddenError) {
console.error('Insufficient permissions for this resource');
} else if (error instanceof ElevenLabsTimeoutError) {
console.error('Request timed out - try increasing timeout');
} else {
console.error('Unexpected error:', error);
}
}try {
const audio = await client.textToSpeech.convert(voiceId, { text: "Hello" });
} catch (error) {
if (error instanceof ElevenLabsError) {
// Access status code
console.log('Status:', error.statusCode);
// Access response body with error details
console.log('Error body:', error.body);
// Access raw HTTP response
if (error.rawResponse) {
console.log('Response headers:', error.rawResponse.headers);
}
}
}async function withRetry<T>(
fn: () => Promise<T>,
maxAttempts: number = 3,
delayMs: number = 1000
): Promise<T> {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
// Retry on timeout or server errors (5xx)
const shouldRetry =
error instanceof ElevenLabsTimeoutError ||
(error instanceof ElevenLabsError && error.statusCode >= 500);
if (shouldRetry && attempt < maxAttempts) {
await new Promise(resolve => setTimeout(resolve, delayMs * attempt));
continue;
}
throw error;
}
}
throw new Error('Max retries exceeded');
}
// Usage
const audio = await withRetry(() =>
client.textToSpeech.convert(voiceId, { text: "Hello" })
);Cause: Invalid or missing API key Solution: Check your API key is correct and has not expired
try {
const audio = await client.textToSpeech.convert(voiceId, { text: "Hello" });
} catch (error) {
if (error instanceof ElevenLabs.UnauthorizedError) {
// Refresh API key or prompt user to re-authenticate
console.error('Authentication failed - please check your API key');
}
}Cause: Resource (voice, history item, etc.) does not exist Solution: Verify the ID is correct
try {
const voice = await client.voices.get(voiceId);
} catch (error) {
if (error instanceof ElevenLabs.NotFoundError) {
console.error(`Voice ${voiceId} not found`);
// Fallback to default voice or list available voices
}
}Cause: Validation error - most common error type Solution: Check error.body for validation details
try {
const audio = await client.textToSpeech.convert(voiceId, {
text: "", // Invalid: empty text
});
} catch (error) {
if (error instanceof ElevenLabs.UnprocessableEntityError) {
console.error('Validation error:', error.body);
// error.body typically contains field-specific error messages
}
}Cause: Request exceeded configured timeout Solution: Increase timeout or optimize request
try {
const audio = await client.music.compose({
prompt: "Epic orchestral soundtrack",
duration: 300, // 5 minutes
}, {
timeoutInSeconds: 600, // Increase timeout for long requests
});
} catch (error) {
if (error instanceof ElevenLabsTimeoutError) {
console.error('Request timed out - consider shorter duration or longer timeout');
}
}Cause: Insufficient permissions or tier restrictions Solution: Check subscription tier or resource access
try {
const audio = await client.textToSpeech.convert(voiceId, {
text: "Hello",
outputFormat: "pcm_44100", // Requires Pro+ tier
});
} catch (error) {
if (error instanceof ElevenLabs.ForbiddenError) {
console.error('Feature requires higher subscription tier or different permissions');
}
}async function synthesizeSpeech(text: string, preferredVoiceId: string, fallbackVoiceId: string) {
try {
return await client.textToSpeech.convert(preferredVoiceId, { text });
} catch (error) {
if (error instanceof ElevenLabs.NotFoundError || error instanceof ElevenLabs.ForbiddenError) {
console.warn('Falling back to default voice');
return await client.textToSpeech.convert(fallbackVoiceId, { text });
}
throw error;
}
}function validateTextToSpeechRequest(text: string): void {
if (!text || text.trim().length === 0) {
throw new Error('Text cannot be empty');
}
if (text.length > 5000) {
throw new Error('Text exceeds maximum length of 5000 characters');
}
}
// Use before API call
try {
validateTextToSpeechRequest(text);
const audio = await client.textToSpeech.convert(voiceId, { text });
} catch (error) {
// Handle validation errors before making API call
console.error('Validation error:', error.message);
}