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
- Always Handle Errors: Listen for error events and handle them appropriately
- Provide User Feedback: Show user-friendly error messages
- Log for Debugging: Include detailed error information for debugging
- Implement Recovery: Attempt automatic recovery for recoverable errors
- Monitor Errors: Track error rates and patterns for system health
- Validate Inputs: Prevent errors through input validation
- Graceful Degradation: Provide fallbacks when features fail
- 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.
