Skip to content

Error Handling Documentation ​

MediaFox provides comprehensive error handling with detailed error types, codes, and recovery strategies. This document covers all error types and best practices for error handling.

Error System Overview ​

MediaFox's error handling system includes:

  • Typed Errors: Specific error classes for different failure modes
  • Error Codes: Standardized error codes for programmatic handling
  • Error Context: Additional information for debugging and recovery
  • Error Events: Proper event emission for all error conditions
  • Recovery Strategies: Built-in and customizable error recovery

Error Classes ​

PlayerError ​

Base error class for all MediaFox-specific errors.

typescript
class PlayerError extends Error {
  readonly code: PlayerErrorCode;
  readonly details?: any;
  readonly timestamp: number;
  readonly recoverable: boolean;

  constructor(
    code: PlayerErrorCode,
    message: string,
    details?: any,
    recoverable = false
  ) {
    super(message);
    this.name = 'PlayerError';
    this.code = code;
    this.details = details;
    this.timestamp = Date.now();
    this.recoverable = recoverable;
  }
}

Usage:

typescript
player.on('error', (error) => {
  if (error instanceof PlayerError) {
    console.log('Player Error:', error.code, error.message);
    console.log('Recoverable:', error.recoverable);
    console.log('Details:', error.details);

    if (error.recoverable) {
      attemptRecovery(error);
    } else {
      showFatalError(error);
    }
  }
});

NetworkError ​

Specialized error for network-related failures.

typescript
class NetworkError extends PlayerError {
  readonly url: string;
  readonly status?: number;
  readonly statusText?: string;

  constructor(
    message: string,
    url: string,
    status?: number,
    statusText?: string
  ) {
    super(PlayerErrorCode.NETWORK_ERROR, message, {
      url,
      status,
      statusText
    }, true);

    this.name = 'NetworkError';
    this.url = url;
    this.status = status;
    this.statusText = statusText;
  }
}

Usage:

typescript
player.on('error', (error) => {
  if (error instanceof NetworkError) {
    console.log('Network Error:', error.status, error.statusText);
    console.log('Failed URL:', error.url);

    // Implement retry logic
    if (error.status >= 500) {
      scheduleRetry(error.url);
    } else if (error.status === 404) {
      showResourceNotFound();
    }
  }
});

MediaError ​

Error for media-specific issues (extends standard MediaError).

typescript
class MediaFoxMediaError extends MediaError {
  readonly playerCode: PlayerErrorCode;
  readonly sourceUrl?: string;

  constructor(
    code: number,
    message: string,
    playerCode: PlayerErrorCode,
    sourceUrl?: string
  ) {
    super();
    this.code = code;
    this.message = message;
    this.playerCode = playerCode;
    this.sourceUrl = sourceUrl;
  }
}

Usage:

typescript
player.on('error', (error) => {
  if (error instanceof MediaFoxMediaError) {
    switch (error.code) {
      case MediaError.MEDIA_ERR_ABORTED:
        console.log('Media loading aborted');
        break;
      case MediaError.MEDIA_ERR_NETWORK:
        console.log('Network error during media loading');
        showNetworkError();
        break;
      case MediaError.MEDIA_ERR_DECODE:
        console.log('Media decode error');
        tryAlternativeSource();
        break;
      case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
        console.log('Media format not supported');
        showUnsupportedFormatError();
        break;
    }
  }
});

SourceError ​

Error for source loading and validation issues.

typescript
class SourceError extends PlayerError {
  readonly source: string;
  readonly sourceType?: string;

  constructor(
    message: string,
    source: string,
    code: PlayerErrorCode,
    sourceType?: string
  ) {
    super(code, message, { source, sourceType }, true);
    this.name = 'SourceError';
    this.source = source;
    this.sourceType = sourceType;
  }
}

Error Codes ​

PlayerErrorCode Enum ​

typescript
enum PlayerErrorCode {
  // Generic errors
  UNKNOWN = 'UNKNOWN',
  INVALID_STATE = 'INVALID_STATE',
  OPERATION_ABORTED = 'OPERATION_ABORTED',
  PERMISSION_DENIED = 'PERMISSION_DENIED',

  // Network errors
  NETWORK_ERROR = 'NETWORK_ERROR',
  NETWORK_TIMEOUT = 'NETWORK_TIMEOUT',
  NETWORK_ABORT = 'NETWORK_ABORT',

  // Source errors
  SOURCE_NOT_FOUND = 'SOURCE_NOT_FOUND',
  SOURCE_INVALID = 'SOURCE_INVALID',
  SOURCE_CORRUPTED = 'SOURCE_CORRUPTED',
  SOURCE_UNSUPPORTED = 'SOURCE_UNSUPPORTED',

  // Media errors
  MEDIA_FORMAT_ERROR = 'MEDIA_FORMAT_ERROR',
  MEDIA_DECODE_ERROR = 'MEDIA_DECODE_ERROR',
  MEDIA_UNSUPPORTED = 'MEDIA_UNSUPPORTED',

  // Track errors
  TRACK_NOT_FOUND = 'TRACK_NOT_FOUND',
  TRACK_SWITCH_FAILED = 'TRACK_SWITCH_FAILED',

  // Memory errors
  OUT_OF_MEMORY = 'OUT_OF_MEMORY',
  MEMORY_LIMIT_EXCEEDED = 'MEMORY_LIMIT_EXCEEDED',

  // Codec errors
  CODEC_NOT_SUPPORTED = 'CODEC_NOT_SUPPORTED',
  CODEC_INITIALIZATION_FAILED = 'CODEC_INITIALIZATION_FAILED',

  // Canvas errors
  CANVAS_CONTEXT_LOST = 'CANVAS_CONTEXT_LOST',
  CANVAS_RENDERING_FAILED = 'CANVAS_RENDERING_FAILED'
}

Error Handling Patterns ​

Basic Error Handling ​

typescript
import { MediaFox, PlayerError, NetworkError, MediaError } from '@mediafox/core';

const player = new MediaFox({ canvas: canvasElement });

player.on('error', (error) => {
  console.error('Player error occurred:', error);

  // Log error details
  logError({
    type: error.constructor.name,
    code: error.code || 'UNKNOWN',
    message: error.message,
    timestamp: Date.now(),
    playerState: player.store.getState()
  });

  // Handle different error types
  handlePlayerError(error);
});

function handlePlayerError(error: Error) {
  if (error instanceof NetworkError) {
    handleNetworkError(error);
  } else if (error instanceof MediaError) {
    handleMediaError(error);
  } else if (error instanceof PlayerError) {
    handleGenericPlayerError(error);
  } else {
    handleUnknownError(error);
  }
}

Network Error Handling ​

typescript
function handleNetworkError(error: NetworkError) {
  const { status, url } = error;

  switch (status) {
    case 404:
      showError('Video not found', 'The requested video could not be found.');
      suggestAlternatives();
      break;

    case 403:
      showError('Access denied', 'You do not have permission to view this video.');
      promptLogin();
      break;

    case 500:
    case 502:
    case 503:
      showError('Server error', 'There was a problem with the video server.');
      scheduleRetry(url, 5000); // Retry in 5 seconds
      break;

    case 0: // Network offline
      showError('No internet connection', 'Please check your internet connection.');
      waitForOnline();
      break;

    default:
      showError('Network error', `Failed to load video: ${error.message}`);
      offerRetry();
  }
}

function scheduleRetry(url: string, delay: number) {
  setTimeout(() => {
    player.load(url).catch(handlePlayerError);
  }, delay);
}

function waitForOnline() {
  window.addEventListener('online', () => {
    player.load(player.currentSrc).catch(handlePlayerError);
  }, { once: true });
}

Media Error Handling ​

typescript
function handleMediaError(error: MediaError | MediaFoxMediaError) {
  switch (error.code) {
    case MediaError.MEDIA_ERR_ABORTED:
      // User aborted loading - usually not an error to show
      console.log('Media loading was aborted by user');
      break;

    case MediaError.MEDIA_ERR_NETWORK:
      showError(
        'Network error',
        'There was a problem downloading the video.'
      );
      offerRetry();
      break;

    case MediaError.MEDIA_ERR_DECODE:
      showError(
        'Video error',
        'There was a problem playing the video file.'
      );
      tryAlternativeFormats();
      break;

    case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
      showError(
        'Unsupported format',
        'This video format is not supported by your browser.'
      );
      suggestBrowserUpdate();
      break;

    default:
      showError('Playback error', error.message);
  }
}

function tryAlternativeFormats() {
  const alternatives = getAlternativeFormats(player.currentSrc);
  if (alternatives.length > 0) {
    player.load(alternatives[0]).catch(handlePlayerError);
  }
}

Recovery Strategies ​

typescript
class ErrorRecoveryManager {
  private retryCount = 0;
  private maxRetries = 3;
  private retryDelay = 1000;

  constructor(private player: MediaFox) {
    this.setupErrorHandling();
  }

  private setupErrorHandling() {
    this.player.on('error', this.handleError.bind(this));
  }

  private async handleError(error: Error) {
    if (!this.isRecoverable(error)) {
      this.showFatalError(error);
      return;
    }

    if (this.retryCount >= this.maxRetries) {
      this.showRetryLimitError(error);
      return;
    }

    await this.attemptRecovery(error);
  }

  private isRecoverable(error: Error): boolean {
    if (error instanceof PlayerError) {
      return error.recoverable;
    }

    if (error instanceof NetworkError) {
      // Network errors are generally recoverable
      return true;
    }

    if (error instanceof MediaError) {
      // Some media errors are recoverable
      return error.code === MediaError.MEDIA_ERR_NETWORK;
    }

    return false;
  }

  private async attemptRecovery(error: Error) {
    this.retryCount++;
    const delay = this.retryDelay * Math.pow(2, this.retryCount - 1); // Exponential backoff

    console.log(`Attempting recovery (${this.retryCount}/${this.maxRetries}) in ${delay}ms`);

    setTimeout(async () => {
      try {
        if (error instanceof NetworkError) {
          await this.recoverFromNetworkError(error);
        } else if (error instanceof MediaError) {
          await this.recoverFromMediaError(error);
        } else {
          await this.genericRecovery();
        }

        // Reset retry count on successful recovery
        this.retryCount = 0;
      } catch (recoveryError) {
        console.error('Recovery failed:', recoveryError);
        this.handleError(recoveryError);
      }
    }, delay);
  }

  private async recoverFromNetworkError(error: NetworkError) {
    // Try to reload the same source
    await this.player.load(error.url);
  }

  private async recoverFromMediaError(error: MediaError) {
    // Try alternative sources or formats
    const currentSrc = this.player.currentSrc;
    const alternatives = this.getAlternativeSources(currentSrc);

    for (const alternative of alternatives) {
      try {
        await this.player.load(alternative);
        console.log('Successfully recovered with alternative source');
        return;
      } catch (altError) {
        console.warn('Alternative source failed:', altError);
      }
    }

    throw new Error('No working alternative sources found');
  }

  private async genericRecovery() {
    // Reinitialize the player
    const currentSrc = this.player.currentSrc;
    const currentTime = this.player.currentTime;

    await this.player.load(currentSrc);
    if (currentTime > 0) {
      this.player.seek(currentTime);
    }
  }

  private getAlternativeSources(originalSrc: string): string[] {
    // Implementation depends on your source structure
    // Could return different qualities, formats, or CDN endpoints
    return [];
  }

  private showFatalError(error: Error) {
    console.error('Fatal error - cannot recover:', error);
    // Show user-friendly error message
  }

  private showRetryLimitError(error: Error) {
    console.error('Retry limit exceeded:', error);
    // Show retry limit message with manual retry option
  }
}

Custom Error Handling ​

typescript
class CustomErrorHandler {
  private errorQueue: Error[] = [];
  private isProcessing = false;

  constructor(private player: MediaFox) {
    this.setupErrorHandler();
  }

  private setupErrorHandler() {
    this.player.on('error', (error) => {
      this.queueError(error);
    });
  }

  private queueError(error: Error) {
    this.errorQueue.push(error);

    if (!this.isProcessing) {
      this.processErrorQueue();
    }
  }

  private async processErrorQueue() {
    this.isProcessing = true;

    while (this.errorQueue.length > 0) {
      const error = this.errorQueue.shift()!;
      await this.processError(error);
    }

    this.isProcessing = false;
  }

  private async processError(error: Error) {
    // Log to analytics
    this.logToAnalytics(error);

    // Report to error tracking service
    await this.reportError(error);

    // Show user notification
    this.showUserNotification(error);

    // Attempt automatic recovery
    await this.attemptAutoRecovery(error);
  }

  private logToAnalytics(error: Error) {
    // Send error data to analytics service
    analytics.track('player_error', {
      error_type: error.constructor.name,
      error_code: (error as any).code || 'UNKNOWN',
      error_message: error.message,
      player_state: this.player.store.getState(),
      user_agent: navigator.userAgent,
      timestamp: Date.now()
    });
  }

  private async reportError(error: Error) {
    try {
      await fetch('/api/errors', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          error: {
            name: error.name,
            message: error.message,
            stack: error.stack,
            code: (error as any).code
          },
          context: {
            userAgent: navigator.userAgent,
            url: window.location.href,
            timestamp: Date.now(),
            playerState: this.player.store.getState()
          }
        })
      });
    } catch (reportError) {
      console.error('Failed to report error:', reportError);
    }
  }

  private showUserNotification(error: Error) {
    const userFriendlyMessage = this.getUserFriendlyMessage(error);
    showToast(userFriendlyMessage, 'error');
  }

  private getUserFriendlyMessage(error: Error): string {
    if (error instanceof NetworkError) {
      return 'Connection problem. Please check your internet connection.';
    }

    if (error instanceof MediaError) {
      switch (error.code) {
        case MediaError.MEDIA_ERR_DECODE:
          return 'There was a problem with the video file.';
        case MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED:
          return 'This video format is not supported.';
        default:
          return 'There was a problem playing the video.';
      }
    }

    return 'An unexpected error occurred.';
  }

  private async attemptAutoRecovery(error: Error) {
    // Implement automatic recovery logic
    // This could include retrying, switching sources, etc.
  }
}

Error Prevention ​

Validation ​

typescript
class ErrorPrevention {
  static validateSource(source: any): void {
    if (!source) {
      throw new SourceError('Source is required', '', PlayerErrorCode.SOURCE_INVALID);
    }

    if (typeof source === 'string') {
      try {
        new URL(source);
      } catch {
        throw new SourceError('Invalid URL format', source, PlayerErrorCode.SOURCE_INVALID);
      }
    }
  }

  static validateCanvas(canvas: any): void {
    if (!(canvas instanceof HTMLCanvasElement)) {
      throw new PlayerError(
        PlayerErrorCode.INVALID_STATE,
        'Canvas must be an HTMLCanvasElement'
      );
    }

    if (!canvas.getContext) {
      throw new PlayerError(
        PlayerErrorCode.CANVAS_CONTEXT_LOST,
        'Canvas context is not available'
      );
    }
  }

  static validatePlayerState(player: MediaFox, operation: string): void {
    if (player.destroyed) {
      throw new PlayerError(
        PlayerErrorCode.INVALID_STATE,
        `Cannot ${operation} on destroyed player`
      );
    }
  }
}

// Usage in MediaFox methods
async load(source: SourceInput) {
  ErrorPrevention.validatePlayerState(this, 'load');
  ErrorPrevention.validateSource(source);

  try {
    // Loading logic...
  } catch (error) {
    this.emit('error', error);
    throw error;
  }
}

Best Practices ​

  1. Always Handle Errors: Listen for error events and handle them appropriately
  2. Provide User Feedback: Show user-friendly error messages
  3. Log for Debugging: Include detailed error information for debugging
  4. Implement Recovery: Attempt automatic recovery for recoverable errors
  5. Monitor Errors: Track error rates and patterns for system health
  6. Validate Inputs: Prevent errors through input validation
  7. Graceful Degradation: Provide fallbacks when features fail
  8. Report Errors: Send error reports to monitoring services

Error handling in MediaFox is designed to be comprehensive, providing both automatic recovery mechanisms and detailed information for custom error handling strategies.

MIT License