CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-mux--mux-node

Official TypeScript library providing comprehensive client access to Mux's video infrastructure API including asset management, live streaming, analytics, and webhook handling

Overview
Eval results
Files

error-handling.mddocs/

Error 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.

Error Class Hierarchy

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;
}

HTTP Status Error Classes

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.
}

Network and Connection Errors

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;
}

Error Handling Patterns

Basic Error Handling

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);
    }
  }
}

Detailed Error Information

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);
      }
    }
  }
}

Retry Logic with Error Handling

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' }],
  })
);

Validation Error Handling

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}`);
        }
      }
    }
  }
}

Upload Error Handling

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);
    }
  }
}

Webhook Signature Verification Errors

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' };
}

Error Recovery Patterns

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...');
  }
}

Error Logging and Monitoring

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;
}

Error Prevention Best Practices

Input Validation

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);
}

Types

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-node

docs

analytics-metrics.md

client-setup.md

data.md

delivery-usage.md

error-handling.md

index.md

jwt-signing.md

jwt.md

live-streaming.md

playback-control.md

system-operations.md

system.md

transcription-vocabularies.md

upload-utilities.md

video-assets.md

video-playback.md

video-uploads.md

video.md

web-inputs.md

webhooks.md

tile.json