Official TypeScript library providing comprehensive client access to Mux's video infrastructure API including asset management, live streaming, analytics, and webhook handling
Comprehensive error classes and handling patterns for different failure scenarios in the Mux Node SDK including network errors, authentication issues, and API-specific errors.
All Mux SDK errors extend from the base MuxError class, providing consistent error handling across all operations.
/**
* Base error class for all Mux SDK errors
*/
class MuxError extends Error {
/** Error name identifier */
override readonly name: string;
/** Error message */
override readonly message: string;
/** Stack trace */
override readonly stack?: string;
}
/**
* Generic API error with HTTP details
*/
class APIError extends MuxError {
/** HTTP status code */
readonly status: number | undefined;
/** Response headers */
readonly headers: Core.Headers | undefined;
/** Error response body */
readonly error: Object | undefined;
}Specific error classes for different HTTP status codes with detailed context information.
/**
* 400 Bad Request - Invalid request parameters
*/
class BadRequestError extends APIError {
override readonly status: 400;
}
/**
* 401 Unauthorized - Authentication failed
*/
class AuthenticationError extends APIError {
override readonly status: 401;
}
/**
* 403 Forbidden - Access denied for resource
*/
class PermissionDeniedError extends APIError {
override readonly status: 403;
}
/**
* 404 Not Found - Resource not found
*/
class NotFoundError extends APIError {
override readonly status: 404;
}
/**
* 409 Conflict - Resource conflict (duplicate creation, etc.)
*/
class ConflictError extends APIError {
override readonly status: 409;
}
/**
* 422 Unprocessable Entity - Validation errors
*/
class UnprocessableEntityError extends APIError {
override readonly status: 422;
}
/**
* 429 Too Many Requests - Rate limit exceeded
*/
class RateLimitError extends APIError {
override readonly status: 429;
/** Retry after seconds */
readonly retryAfter?: number;
}
/**
* 500+ Server Error - Server-side errors
*/
class InternalServerError extends APIError {
override readonly status: number; // 500, 502, 503, etc.
}Error classes for network-related failures and connection issues.
/**
* Network connection failure
*/
class APIConnectionError extends MuxError {
/** Original network error */
readonly cause?: Error;
}
/**
* Request timeout
*/
class APIConnectionTimeoutError extends APIConnectionError {
/** Timeout duration in milliseconds */
readonly timeout?: number;
}
/**
* Request aborted by user
*/
class APIUserAbortError extends APIConnectionError {
/** Abort reason */
readonly reason?: string;
}import {
MuxError,
AuthenticationError,
NotFoundError,
BadRequestError,
} from '@mux/mux-node';
async function handleBasicErrors() {
try {
const asset = await mux.video.assets.retrieve('asset-id');
console.log('Asset retrieved:', asset.id);
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Authentication failed - check your token credentials');
} else if (error instanceof NotFoundError) {
console.error('Asset not found');
} else if (error instanceof BadRequestError) {
console.error('Invalid request parameters');
} else if (error instanceof MuxError) {
console.error('Mux API error:', error.message);
} else {
console.error('Unexpected error:', error);
}
}
}import { APIError } from '@mux/mux-node';
async function handleDetailedErrors() {
try {
const asset = await mux.video.assets.create({
inputs: [{ url: 'invalid-url' }],
});
} catch (error) {
if (error instanceof APIError) {
console.error('API Error Details:');
console.error('Status:', error.status);
console.error('Message:', error.message);
console.error('Headers:', error.headers);
console.error('Error Body:', error.error);
// Access detailed error information
if (error.error && typeof error.error === 'object') {
const errorBody = error.error as any;
console.error('Error Code:', errorBody.code);
console.error('Error Details:', errorBody.details);
}
}
}
}import {
RateLimitError,
APIConnectionTimeoutError,
InternalServerError,
} from '@mux/mux-node';
async function withRetry<T>(
operation: () => Promise<T>,
maxRetries: number = 3
): Promise<T> {
let lastError: Error;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error as Error;
// Determine if error is retryable
const isRetryable = error instanceof RateLimitError ||
error instanceof APIConnectionTimeoutError ||
error instanceof InternalServerError;
if (!isRetryable || attempt === maxRetries) {
throw error;
}
// Calculate delay based on error type
let delay = Math.pow(2, attempt - 1) * 1000; // Exponential backoff
if (error instanceof RateLimitError && error.retryAfter) {
delay = error.retryAfter * 1000;
}
console.log(`Attempt ${attempt} failed, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError!;
}
// Usage
const asset = await withRetry(() =>
mux.video.assets.create({
inputs: [{ url: 'https://example.com/video.mp4' }],
})
);import { UnprocessableEntityError } from '@mux/mux-node';
async function handleValidationErrors() {
try {
const asset = await mux.video.assets.create({
inputs: [{ url: '' }], // Invalid empty URL
playback_policies: ['invalid-policy'], // Invalid policy
});
} catch (error) {
if (error instanceof UnprocessableEntityError) {
console.error('Validation errors occurred:');
// Parse validation errors from response body
const errorBody = error.error as any;
if (errorBody?.errors) {
for (const validationError of errorBody.errors) {
console.error(`Field: ${validationError.field}`);
console.error(`Message: ${validationError.message}`);
console.error(`Code: ${validationError.code}`);
}
}
}
}
}import { APIConnectionError, BadRequestError } from '@mux/mux-node';
async function handleUploadErrors() {
try {
const upload = await mux.video.uploads.create({
cors_origin: 'https://example.com',
});
// Monitor upload status with error handling
const checkUploadStatus = async (): Promise<string> => {
while (true) {
try {
const uploadStatus = await mux.video.uploads.retrieve(upload.id);
switch (uploadStatus.status) {
case 'asset_created':
return uploadStatus.asset_id!;
case 'errored':
throw new Error(`Upload failed: ${uploadStatus.error?.message}`);
case 'cancelled':
throw new Error('Upload was cancelled');
case 'timed_out':
throw new Error('Upload timed out');
case 'waiting':
await new Promise(resolve => setTimeout(resolve, 1000));
break;
}
} catch (error) {
if (error instanceof APIConnectionError) {
console.warn('Connection error checking upload status, retrying...');
await new Promise(resolve => setTimeout(resolve, 5000));
} else {
throw error;
}
}
}
};
const assetId = await checkUploadStatus();
console.log('Upload completed, asset created:', assetId);
} catch (error) {
if (error instanceof BadRequestError) {
console.error('Invalid upload parameters:', error.message);
} else {
console.error('Upload error:', error);
}
}
}async function handleWebhookErrors(body: string, headers: Record<string, string>) {
try {
const event = mux.webhooks.unwrap(body, headers);
console.log('Webhook event:', event.type);
} catch (error) {
if (error instanceof MuxError) {
// Webhook signature verification failed
console.error('Invalid webhook signature');
// Return 400 status to webhook sender
return { status: 400, message: 'Invalid signature' };
} else {
console.error('Webhook processing error:', error);
// Return 500 status for processing errors
return { status: 500, message: 'Processing error' };
}
}
return { status: 200, message: 'OK' };
}import { AuthenticationError, NotFoundError } from '@mux/mux-node';
class MuxService {
private fallbackAssetId = 'default-asset-id';
async getAssetWithFallback(assetId: string) {
try {
return await mux.video.assets.retrieve(assetId);
} catch (error) {
if (error instanceof NotFoundError) {
console.warn(`Asset ${assetId} not found, using fallback`);
return await mux.video.assets.retrieve(this.fallbackAssetId);
} else if (error instanceof AuthenticationError) {
console.error('Authentication error - token may be expired');
await this.refreshAuthToken();
return await mux.video.assets.retrieve(assetId);
} else {
throw error;
}
}
}
private async refreshAuthToken() {
// Implement token refresh logic
console.log('Refreshing authentication token...');
}
}import { APIError, APIConnectionError } from '@mux/mux-node';
function logError(error: Error, context?: Record<string, any>) {
const logData: Record<string, any> = {
timestamp: new Date().toISOString(),
error_type: error.constructor.name,
message: error.message,
stack: error.stack,
...context,
};
if (error instanceof APIError) {
logData.status = error.status;
logData.headers = error.headers;
logData.error_body = error.error;
}
if (error instanceof APIConnectionError) {
logData.network_error = true;
logData.cause = error.cause?.message;
}
// Send to logging service
console.error('Mux SDK Error:', JSON.stringify(logData, null, 2));
// Could also send to external monitoring service
// sendToMonitoring(logData);
}
// Usage in error handlers
try {
await mux.video.assets.create(params);
} catch (error) {
logError(error as Error, {
operation: 'asset_creation',
params: params,
user_id: 'user-123',
});
throw error;
}function validateAssetInput(input: any): string[] {
const errors: string[] = [];
if (!input.inputs || !Array.isArray(input.inputs) || input.inputs.length === 0) {
errors.push('At least one input is required');
}
for (const inputItem of input.inputs || []) {
if (!inputItem.url || typeof inputItem.url !== 'string') {
errors.push('Input URL is required and must be a string');
}
try {
new URL(inputItem.url);
} catch {
errors.push(`Invalid URL format: ${inputItem.url}`);
}
}
if (input.playback_policies) {
const validPolicies = ['public', 'signed', 'drm'];
for (const policy of input.playback_policies) {
if (!validPolicies.includes(policy)) {
errors.push(`Invalid playback policy: ${policy}`);
}
}
}
return errors;
}
async function createAssetSafely(input: any) {
const validationErrors = validateAssetInput(input);
if (validationErrors.length > 0) {
throw new Error(`Validation failed: ${validationErrors.join(', ')}`);
}
return await mux.video.assets.create(input);
}interface ErrorResponse {
/** Error code identifier */
code?: string;
/** Human-readable error message */
message: string;
/** Detailed error information */
details?: Record<string, any>;
/** Field-specific validation errors */
errors?: Array<ValidationError>;
}
interface ValidationError {
/** Field that failed validation */
field: string;
/** Validation error message */
message: string;
/** Validation error code */
code: string;
/** Invalid value */
rejected_value?: any;
}
interface RetryConfiguration {
/** Maximum number of retry attempts */
max_retries: number;
/** Initial delay in milliseconds */
initial_delay: number;
/** Maximum delay in milliseconds */
max_delay: number;
/** Backoff multiplier */
backoff_factor: number;
}Install with Tessl CLI
npx tessl i tessl/npm-mux--mux-nodedocs