Now.js Framework Documentation

Now.js Framework Documentation

AuthErrorHandler - Error Management for Authentication

EN 31 Oct 2025 01:28

AuthErrorHandler - Error Management for Authentication

Documentation for AuthErrorHandler, the error management system for authentication in the Now.js Framework

📋 Table of Contents

  1. Overview
  2. Installation and Import
  3. Getting Started
  4. Error Types
  5. Error Actions
  6. Custom Error Handling
  7. Error Events
  8. Retry Logic
  9. Error Recovery
  10. Usage Examples
  11. API Reference
  12. Best Practices
  13. Common Pitfalls

Overview

AuthErrorHandler manages errors and exceptions that occur during the authentication process, providing appropriate responses and recovery mechanisms.

Key Features

  • Error Classification: Clear categorization of errors
  • Configurable Actions: Define actions for each error type
  • Custom Handlers: Support for custom error handlers
  • Retry Logic: Automatic retry for specific error types
  • Error Recovery: Recovery mechanisms from errors
  • Event System: Events for error tracking
  • User Notifications: Notify users when errors occur
  • Error Logging: Record errors for debugging
  • Graceful Degradation: Gracefully degrade features
  • Error Boundaries: Limit the scope of errors

When to Use AuthErrorHandler

Use AuthErrorHandler when:

  • Need systematic authentication error management
  • Want custom error handling logic
  • Need to track and log errors
  • Want retry logic for errors
  • Need to notify users when errors occur

Don't use when:

  • Need to handle all errors manually
  • Not using authentication system

Installation and Import

AuthErrorHandler is loaded with the Now.js Framework and is ready to use immediately via the window object:

// No import needed - ready to use immediately
console.log(window.AuthErrorHandler); // AuthErrorHandler object

Getting Started

Basic Setup

// AuthErrorHandler works automatically with AuthManager
// No separate initialization needed

await AuthManager.init({
  enabled: true,
  endpoints: {
    login: '/api/auth/login',
    verify: '/api/auth/verify'
  },

  // Error handling configuration
  errorHandling: {
    // Max retry attempts
    maxRetries: 3,

    // Retry delay (ms)
    retryDelay: 1000,

    // Show user notifications
    showNotifications: true,

    // Log errors to console
    logErrors: true
  }
});

console.log('AuthErrorHandler initialized with AuthManager');

Error Handling Flow

Operation fails
     ↓
┌────────────────────┐
│  Classify Error    │
│  - Network?        │
│  - Authentication? │
│  - Authorization?  │
│  - Validation?     │
└──────┬─────────────┘
       ↓
┌────────────────────┐
│ Determine Action   │
│  - Retry?          │
│  - Redirect?       │
│  - Notify?         │
│  - Log?            │
└──────┬─────────────┘
       ↓
┌────────────────────┐
│  Execute Action    │
│  - Retry operation │
│  - Navigate away   │
│  - Show message    │
│  - Log error       │
└──────┬─────────────┘
       ↓
┌────────────────────┐
│ Emit Error Event   │
│  - Custom handlers │
│  - Tracking        │
│  - Logging         │
└────────────────────┘

Error Types

AuthErrorHandler supports the following main error types:

1. NETWORK_ERROR

Connection or network failure

// Triggered when:
// - No internet connection
// - Server unreachable
// - Request timeout
// - CORS errors

// Default action: Retry with exponential backoff
{
  type: 'NETWORK_ERROR',
  message: 'Network connection failed',
  retryable: true,
  action: 'retry'
}

2. UNAUTHORIZED

Not authenticated or token expired

// Triggered when:
// - No token provided
// - Token expired
// - Invalid token
// - Token revoked

// Default action: Redirect to login
{
  type: 'UNAUTHORIZED',
  message: 'Authentication required',
  retryable: false,
  action: 'redirect',
  target: '/login'
}

3. FORBIDDEN

Authenticated but no permission

// Triggered when:
// - Missing required role
// - Missing required permission
// - Access denied by policy

// Default action: Show error page
{
  type: 'FORBIDDEN',
  message: 'Access denied',
  retryable: false,
  action: 'render',
  target: '/403'
}

4. VALIDATION_ERROR

Data does not meet requirements

// Triggered when:
// - Invalid credentials
// - Missing required fields
// - Invalid format

// Default action: Show validation errors
{
  type: 'VALIDATION_ERROR',
  message: 'Invalid input',
  errors: {
    email: 'Invalid email format',
    password: 'Password too short'
  },
  retryable: true,
  action: 'notify'
}

5. TOKEN_EXPIRED

Token has expired

// Triggered when:
// - Access token expired
// - Refresh token expired

// Default action: Try refresh, then redirect
{
  type: 'TOKEN_EXPIRED',
  message: 'Token expired',
  retryable: true,
  action: 'refresh',
  fallback: 'redirect',
  target: '/login'
}

6. TOKEN_REFRESH_FAILED

Token refresh failed

// Triggered when:
// - Refresh token invalid
// - Refresh endpoint failed
// - No refresh token

// Default action: Redirect to login
{
  type: 'TOKEN_REFRESH_FAILED',
  message: 'Failed to refresh token',
  retryable: false,
  action: 'redirect',
  target: '/login'
}

7. SESSION_EXPIRED

Session has expired

// Triggered when:
// - Session timeout
// - Session invalidated
// - Logged out from another device

// Default action: Redirect to login with message
{
  type: 'SESSION_EXPIRED',
  message: 'Your session has expired',
  retryable: false,
  action: 'redirect',
  target: '/login',
  notify: true
}

8. RATE_LIMIT_EXCEEDED

Rate limit exceeded

// Triggered when:
// - Too many requests
// - Rate limit exceeded

// Default action: Retry after delay
{
  type: 'RATE_LIMIT_EXCEEDED',
  message: 'Too many requests',
  retryable: true,
  action: 'retry',
  retryAfter: 60000,  // 1 minute
  notify: true
}

9. SERVER_ERROR

Internal server error

// Triggered when:
// - 500 errors
// - Server exceptions
// - Database errors

// Default action: Retry with limit
{
  type: 'SERVER_ERROR',
  message: 'Server error occurred',
  retryable: true,
  action: 'retry',
  maxRetries: 3
}

10. CSRF_ERROR

CSRF token invalid

// Triggered when:
// - CSRF token missing
// - CSRF token invalid
// - CSRF token expired

// Default action: Refresh CSRF and retry
{
  type: 'CSRF_ERROR',
  message: 'CSRF validation failed',
  retryable: true,
  action: 'refresh_csrf',
  fallback: 'reload'
}

11. CUSTOM_ERROR

Custom application-defined error

// Triggered by application logic
{
  type: 'CUSTOM_ERROR',
  code: 'SUBSCRIPTION_REQUIRED',
  message: 'Active subscription required',
  retryable: false,
  action: 'custom',
  handler: 'handleSubscriptionError'
}

Error Actions

1. Retry Action

// Automatically retry failed operation
{
  action: 'retry',
  maxRetries: 3,
  retryDelay: 1000,
  backoff: 'exponential'  // linear, exponential, fixed
}

// Retry with exponential backoff
// Attempt 1: 1s delay
// Attempt 2: 2s delay
// Attempt 3: 4s delay

2. Redirect Action

// Redirect to another route
{
  action: 'redirect',
  target: '/login',
  reason: 'Authentication required',
  storeIntendedRoute: true  // Store current route
}

// After login, redirect back to intended route

3. Render Action

// Render error page
{
  action: 'render',
  target: '/403',
  context: {
    error: 'Access denied',
    requiredRole: 'admin'
  }
}

4. Notify Action

// Show notification to user
{
  action: 'notify',
  notification: {
    type: 'error',
    title: 'Login Failed',
    message: 'Invalid credentials',
    duration: 5000
  }
}

5. Block Action

// Block navigation, stay on current page
{
  action: 'block',
  reason: 'Unsaved changes',
  confirm: true  // Show confirmation dialog
}

6. Refresh Action

// Refresh tokens
{
  action: 'refresh',
  type: 'token',
  fallback: {
    action: 'redirect',
    target: '/login'
  }
}

7. Logout Action

// Force logout
{
  action: 'logout',
  reason: 'Session invalidated',
  redirect: '/login',
  notify: true
}

8. Custom Action

// Execute custom handler
{
  action: 'custom',
  handler: async (error, context) => {
    console.log('Custom error handler:', error);

    // Custom logic
    if (error.code === 'PAYMENT_REQUIRED') {
      await showPaymentModal();
    }

    return { handled: true };
  }
}

Custom Error Handling

1. Global Error Handler

// Set global error handler
AuthErrorHandler.setGlobalHandler(async (error, context) => {
  console.log('Global error handler:', error.type);

  // Log to external service
  await logErrorToService(error);

  // Custom notification
  if (error.type === 'UNAUTHORIZED') {
    showCustomLoginModal();
    return { handled: true };
  }

  // Let default handler process
  return { handled: false };
});

2. Error Type-Specific Handlers

// Register handler for specific error type
AuthErrorHandler.registerHandler('VALIDATION_ERROR', async (error) => {
  console.log('Validation errors:', error.errors);

  // Show validation errors in form
  Object.keys(error.errors).forEach(field => {
    showFieldError(field, error.errors[field]);
  });

  return { handled: true };
});

// Handler for rate limiting
AuthErrorHandler.registerHandler('RATE_LIMIT_EXCEEDED', async (error) => {
  const retryAfter = error.retryAfter || 60000;
  const minutes = Math.ceil(retryAfter / 60000);

  showNotification(
    `Too many attempts. Please try again in ${minutes} minute(s).`,
    'warning'
  );

  return { handled: true };
});

3. Context-Aware Error Handling

// Handle errors based on context
AuthErrorHandler.setContextHandler(async (error, context) => {
  console.log('Context:', context);

  // Different handling for different operations
  if (context.operation === 'login') {
    if (error.type === 'VALIDATION_ERROR') {
      highlightInvalidFields(error.errors);
      return { handled: true };
    }
  }

  if (context.operation === 'refresh_token') {
    if (error.type === 'TOKEN_REFRESH_FAILED') {
      // Force logout on refresh failure
      await AuthManager.logout();
      return { handled: true };
    }
  }

  return { handled: false };
});

4. Chained Error Handlers

// Multiple handlers in sequence
AuthErrorHandler.chainHandlers([
  // 1. Log all errors
  async (error) => {
    await logError(error);
    return { handled: false };  // Continue chain
  },

  // 2. Track in analytics
  async (error) => {
    trackErrorInAnalytics(error);
    return { handled: false };  // Continue chain
  },

  // 3. Custom handling
  async (error) => {
    if (error.type === 'NETWORK_ERROR') {
      showOfflineMode();
      return { handled: true };  // Stop chain
    }
    return { handled: false };
  },

  // 4. Default handler (only if not handled)
  async (error) => {
    showGenericErrorMessage(error);
    return { handled: true };
  }
]);

Error Events

Available Events

// Listen to error events

// 1. Any error
document.addEventListener('auth:error', (e) => {
  const { error, context } = e.detail;
  console.log('Auth error:', error.type);
});

// 2. Specific error type
document.addEventListener('auth:error:unauthorized', (e) => {
  console.log('User unauthorized');
});

document.addEventListener('auth:error:forbidden', (e) => {
  console.log('Access forbidden');
});

// 3. Network errors
document.addEventListener('auth:error:network', (e) => {
  console.log('Network error');
  showOfflineIndicator();
});

// 4. Validation errors
document.addEventListener('auth:error:validation', (e) => {
  const { errors } = e.detail;
  console.log('Validation errors:', errors);
});

// 5. Token errors
document.addEventListener('auth:error:token', (e) => {
  console.log('Token error');
});

// 6. Session errors
document.addEventListener('auth:error:session', (e) => {
  console.log('Session error');
});

// 7. Rate limit errors
document.addEventListener('auth:error:ratelimit', (e) => {
  const { retryAfter } = e.detail;
  console.log(`Rate limited. Retry after ${retryAfter}ms`);
});

// 8. Server errors
document.addEventListener('auth:error:server', (e) => {
  console.log('Server error');
});

// 9. Error recovery
document.addEventListener('auth:error:recovered', (e) => {
  console.log('Error recovered');
});

// 10. Error retry
document.addEventListener('auth:error:retry', (e) => {
  const { attempt, maxRetries } = e.detail;
  console.log(`Retry attempt ${attempt}/${maxRetries}`);
});

Retry Logic

1. Automatic Retry

// Configure retry behavior
await AuthManager.init({
  errorHandling: {
    maxRetries: 3,
    retryDelay: 1000,
    retryBackoff: 'exponential',  // linear, exponential, fixed

    // Which errors to retry
    retryableErrors: [
      'NETWORK_ERROR',
      'SERVER_ERROR',
      'TOKEN_EXPIRED',
      'RATE_LIMIT_EXCEEDED'
    ]
  }
});

2. Retry Strategies

Exponential Backoff

// Retry delays increase exponentially
// Attempt 1: 1s
// Attempt 2: 2s
// Attempt 3: 4s
// Attempt 4: 8s

{
  retryBackoff: 'exponential',
  retryDelay: 1000,
  maxRetries: 4
}

Linear Backoff

// Retry delays increase linearly
// Attempt 1: 1s
// Attempt 2: 2s
// Attempt 3: 3s
// Attempt 4: 4s

{
  retryBackoff: 'linear',
  retryDelay: 1000,
  maxRetries: 4
}

Fixed Delay

// Same delay for all retries
// Attempt 1: 1s
// Attempt 2: 1s
// Attempt 3: 1s

{
  retryBackoff: 'fixed',
  retryDelay: 1000,
  maxRetries: 3
}

3. Custom Retry Logic

// Custom retry decision
AuthErrorHandler.setRetryDecider(async (error, attempt, context) => {
  console.log(`Retry decision for ${error.type}, attempt ${attempt}`);

  // Custom logic
  if (error.type === 'NETWORK_ERROR') {
    // Check if online
    if (!navigator.onLine) {
      return { retry: false, reason: 'Offline' };
    }

    // Retry up to 5 times for network errors
    if (attempt < 5) {
      return {
        retry: true,
        delay: attempt * 2000  // Custom delay
      };
    }
  }

  if (error.type === 'RATE_LIMIT_EXCEEDED') {
    // Use server's retry-after header
    return {
      retry: true,
      delay: error.retryAfter || 60000
    };
  }

  // Default behavior
  return { retry: false };
});

4. Retry with Circuit Breaker

// Prevent retry storms
const circuitBreaker = {
  failures: 0,
  threshold: 5,
  timeout: 60000,
  state: 'CLOSED'  // CLOSED, OPEN, HALF_OPEN
};

AuthErrorHandler.setRetryDecider(async (error, attempt) => {
  // Check circuit breaker state
  if (circuitBreaker.state === 'OPEN') {
    const now = Date.now();
    const timeSinceOpen = now - circuitBreaker.openedAt;

    if (timeSinceOpen >= circuitBreaker.timeout) {
      // Try half-open
      circuitBreaker.state = 'HALF_OPEN';
    } else {
      // Still open, don't retry
      return {
        retry: false,
        reason: 'Circuit breaker open'
      };
    }
  }

  // Retry logic
  if (error.type === 'SERVER_ERROR') {
    circuitBreaker.failures++;

    if (circuitBreaker.failures >= circuitBreaker.threshold) {
      // Open circuit
      circuitBreaker.state = 'OPEN';
      circuitBreaker.openedAt = Date.now();
      return { retry: false, reason: 'Circuit breaker opened' };
    }

    return { retry: true, delay: 1000 };
  }

  return { retry: false };
});

// Reset on success
document.addEventListener('auth:success', () => {
  if (circuitBreaker.state === 'HALF_OPEN') {
    circuitBreaker.state = 'CLOSED';
    circuitBreaker.failures = 0;
  }
});

Error Recovery

1. Automatic Recovery

// AuthErrorHandler attempts recovery automatically

// For TOKEN_EXPIRED:
// 1. Try refresh token
// 2. If refresh fails, redirect to login

// For NETWORK_ERROR:
// 1. Retry with backoff
// 2. Show offline indicator
// 3. Queue operations for when online

// For SESSION_EXPIRED:
// 1. Clear session data
// 2. Store intended route
// 3. Redirect to login

2. Custom Recovery Strategies

// Register recovery strategy
AuthErrorHandler.registerRecovery('TOKEN_EXPIRED', async (error, context) => {
  console.log('Recovering from token expiration');

  try {
    // Try to refresh token
    await AuthManager.refreshToken();

    // Retry original operation
    if (context.operation) {
      await context.operation.retry();
    }

    return { recovered: true };
  } catch (refreshError) {
    // Refresh failed, redirect to login
    await Router.navigate('/login');
    return { recovered: false };
  }
});

3. Graceful Degradation

// Degrade functionality instead of complete failure
AuthErrorHandler.registerRecovery('NETWORK_ERROR', async (error) => {
  console.log('Network error - enabling offline mode');

  // Enable offline mode
  app.offlineMode = true;

  // Show offline indicator
  showOfflineBanner();

  // Use cached data
  const cachedData = await loadFromCache();
  if (cachedData) {
    renderWithCachedData(cachedData);
    return { recovered: true };
  }

  return { recovered: false };
});

// Recover when online
window.addEventListener('online', async () => {
  if (app.offlineMode) {
    app.offlineMode = false;
    hideOfflineBanner();

    // Sync offline changes
    await syncOfflineChanges();

    // Reload current page
    await Router.reload();
  }
});

Usage Examples

1. Complete Error Handling Setup

// Initialize with comprehensive error handling
await AuthManager.init({
  enabled: true,
  endpoints: {
    login: '/api/auth/login',
    logout: '/api/auth/logout',
    refresh: '/api/auth/refresh',
    verify: '/api/auth/verify'
  },

  errorHandling: {
    // Retry configuration
    maxRetries: 3,
    retryDelay: 1000,
    retryBackoff: 'exponential',

    // Notification configuration
    showNotifications: true,
    notificationDuration: 5000,

    // Logging
    logErrors: true,
    logLevel: 'error',

    // Error types to retry
    retryableErrors: [
      'NETWORK_ERROR',
      'SERVER_ERROR',
      'TOKEN_EXPIRED',
      'RATE_LIMIT_EXCEEDED'
    ]
  }
});

// Listen to all errors
document.addEventListener('auth:error', (e) => {
  const { error, context } = e.detail;

  // Log to analytics
  logErrorToAnalytics({
    type: error.type,
    message: error.message,
    context: context.operation,
    timestamp: new Date().toISOString()
  });
});

// Handle network errors
document.addEventListener('auth:error:network', () => {
  showOfflineIndicator();
});

// Handle unauthorized errors
document.addEventListener('auth:error:unauthorized', () => {
  // Clear any cached auth state
  localStorage.removeItem('authState');
});

console.log('Error handling configured');

2. Custom Validation Error Display

// Register handler for validation errors
AuthErrorHandler.registerHandler('VALIDATION_ERROR', async (error) => {
  console.log('Validation errors:', error.errors);

  // Clear previous errors
  document.querySelectorAll('.error-message').forEach(el => {
    el.remove();
  });

  document.querySelectorAll('.is-invalid').forEach(el => {
    el.classList.remove('is-invalid');
  });

  // Display new errors
  Object.keys(error.errors).forEach(field => {
    const input = document.querySelector(`[name="${field}"]`);
    if (input) {
      // Mark input as invalid
      input.classList.add('is-invalid');

      // Create error message
      const errorDiv = document.createElement('div');
      errorDiv.className = 'error-message text-danger';
      errorDiv.textContent = error.errors[field];

      // Insert after input
      input.parentNode.insertBefore(errorDiv, input.nextSibling);
    }
  });

  // Focus first invalid field
  const firstInvalid = document.querySelector('.is-invalid');
  if (firstInvalid) {
    firstInvalid.focus();
  }

  return { handled: true };
});

// Example login with validation
async function handleLogin(e) {
  e.preventDefault();

  try {
    await AuthManager.login({
      email: emailInput.value,
      password: passwordInput.value
    });

    // Success - redirect
    await Router.navigate('/dashboard');
  } catch (error) {
    // AuthErrorHandler automatically handles validation errors
    console.log('Login failed:', error.type);
  }
}

3. Rate Limiting with User Feedback

// Handle rate limit errors
AuthErrorHandler.registerHandler('RATE_LIMIT_EXCEEDED', async (error) => {
  const retryAfter = error.retryAfter || 60000;
  const minutes = Math.ceil(retryAfter / 60000);

  // Show countdown modal
  const modal = createModal({
    title: 'Too Many Attempts',
    message: `You've made too many login attempts. Please wait ${minutes} minute(s).`,
    countdown: retryAfter,
    cancelable: false
  });

  // Start countdown
  let remaining = retryAfter;
  const interval = setInterval(() => {
    remaining -= 1000;

    if (remaining <= 0) {
      clearInterval(interval);
      modal.close();
      return;
    }

    const secs = Math.ceil(remaining / 1000);
    modal.updateMessage(`Please wait ${secs} seconds...`);
  }, 1000);

  return { handled: true };
});

// Prevent form submission during rate limit
document.addEventListener('auth:error:ratelimit', (e) => {
  const { retryAfter } = e.detail;

  // Disable login form
  const loginForm = document.getElementById('loginForm');
  const submitBtn = loginForm.querySelector('[type="submit"]');

  submitBtn.disabled = true;
  submitBtn.textContent = 'Please wait...';

  // Re-enable after delay
  setTimeout(() => {
    submitBtn.disabled = false;
    submitBtn.textContent = 'Login';
  }, retryAfter);
});

4. Offline Mode with Queue

// Operation queue for offline mode
const operationQueue = [];

// Handle network errors
AuthErrorHandler.registerHandler('NETWORK_ERROR', async (error, context) => {
  console.log('Network error - queueing operation');

  // Queue operation
  if (context.operation) {
    operationQueue.push({
      operation: context.operation,
      timestamp: Date.now(),
      error: error
    });
  }

  // Show offline mode
  showOfflineMode();

  return { handled: true };
});

// Process queue when online
window.addEventListener('online', async () => {
  console.log('Back online - processing queue');
  hideOfflineMode();

  // Process queued operations
  while (operationQueue.length > 0) {
    const item = operationQueue.shift();

    try {
      await item.operation.retry();
      console.log('Operation completed:', item.operation.type);
    } catch (error) {
      console.error('Operation failed:', error);

      // Re-queue if still network error
      if (error.type === 'NETWORK_ERROR') {
        operationQueue.push(item);
        break;  // Stop processing, still offline
      }
    }
  }

  // Show notification
  if (operationQueue.length === 0) {
    showNotification('All operations completed', 'success');
  } else {
    showNotification(`${operationQueue.length} operations pending`, 'warning');
  }
});

// Clear old queued operations (older than 1 hour)
setInterval(() => {
  const now = Date.now();
  const maxAge = 60 * 60 * 1000;  // 1 hour

  const filtered = operationQueue.filter(item => {
    return (now - item.timestamp) < maxAge;
  });

  const removed = operationQueue.length - filtered.length;
  if (removed > 0) {
    console.log(`Removed ${removed} old operations from queue`);
    operationQueue.length = 0;
    operationQueue.push(...filtered);
  }
}, 5 * 60 * 1000);  // Check every 5 minutes

5. Token Refresh with Fallback

// Handle token expiration
AuthErrorHandler.registerHandler('TOKEN_EXPIRED', async (error, context) => {
  console.log('Token expired - attempting refresh');

  try {
    // Try to refresh token
    await AuthManager.refreshToken();

    console.log('Token refreshed successfully');

    // Retry original operation
    if (context.operation) {
      return await context.operation.retry();
    }

    return { handled: true };
  } catch (refreshError) {
    console.error('Token refresh failed:', refreshError);

    // Store intended route
    const currentRoute = Router.getCurrentRoute();
    if (currentRoute && !currentRoute.metadata?.public) {
      AuthGuard.storeIntendedRoute(currentRoute, Router);
    }

    // Redirect to login
    await Router.navigate('/login');

    // Show notification
    showNotification(
      'Your session has expired. Please login again.',
      'warning'
    );

    return { handled: true };
  }
});

// Handle refresh failures
document.addEventListener('auth:error:token_refresh_failed', async () => {
  // Force logout
  await AuthManager.logout();

  // Clear all auth data
  localStorage.clear();
  sessionStorage.clear();

  // Redirect to login
  await Router.navigate('/login');
});

6. Error Tracking and Analytics

// Track all errors in analytics
document.addEventListener('auth:error', async (e) => {
  const { error, context } = e.detail;

  // Prepare error data
  const errorData = {
    type: error.type,
    message: error.message,
    context: {
      operation: context.operation?.type,
      route: Router.getCurrentRoute()?.path,
      user: AuthManager.getUser()?.id
    },
    timestamp: new Date().toISOString(),
    userAgent: navigator.userAgent,
    url: window.location.href
  };

  // Send to analytics
  try {
    await fetch('/api/analytics/error', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(errorData)
    });
  } catch (err) {
    console.error('Failed to log error:', err);
  }

  // Log to console in development
  if (isDevelopment) {
    console.group('Auth Error');
    console.error('Type:', error.type);
    console.error('Message:', error.message);
    console.error('Context:', context);
    console.groupEnd();
  }
});

// Track error recovery
document.addEventListener('auth:error:recovered', (e) => {
  const { error, recoveryMethod } = e.detail;

  trackEvent('auth_error_recovered', {
    error_type: error.type,
    recovery_method: recoveryMethod
  });
});

// Track retry attempts
document.addEventListener('auth:error:retry', (e) => {
  const { error, attempt, maxRetries } = e.detail;

  trackEvent('auth_error_retry', {
    error_type: error.type,
    attempt: attempt,
    max_retries: maxRetries
  });
});

API Reference

Methods

handleError(error, context)

Handle an error with configured actions

Parameters:

  • error (Object/Error) - Error object
  • context (Object) - Error context

Returns: Promise<Object> - Handling result

Example:

try {
  await AuthManager.login(credentials);
} catch (error) {
  await AuthErrorHandler.handleError(error, {
    operation: 'login',
    route: Router.getCurrentRoute()
  });
}

registerHandler(errorType, handler)

Register handler for specific error type

Parameters:

  • errorType (string) - Error type
  • handler (Function) - Handler function

Returns: void

Example:

AuthErrorHandler.registerHandler('VALIDATION_ERROR', async (error) => {
  displayValidationErrors(error.errors);
  return { handled: true };
});

setGlobalHandler(handler)

Set global error handler (called for all errors)

Parameters:

  • handler (Function) - Global handler function

Returns: void

Example:

AuthErrorHandler.setGlobalHandler(async (error, context) => {
  await logErrorToService(error);
  return { handled: false };  // Continue to specific handlers
});

setRetryDecider(decider)

Set custom retry decision logic

Parameters:

  • decider (Function) - Retry decision function

Returns: void

Example:

AuthErrorHandler.setRetryDecider(async (error, attempt) => {
  if (error.type === 'NETWORK_ERROR' && attempt < 5) {
    return { retry: true, delay: attempt * 1000 };
  }
  return { retry: false };
});

registerRecovery(errorType, strategy)

Register recovery strategy for error type

Parameters:

  • errorType (string) - Error type
  • strategy (Function) - Recovery strategy function

Returns: void

Example:

AuthErrorHandler.registerRecovery('TOKEN_EXPIRED', async (error, context) => {
  await AuthManager.refreshToken();
  return { recovered: true };
});

clearHandlers()

Clear all registered error handlers

Returns: void

Example:

AuthErrorHandler.clearHandlers();

Best Practices

1. Handle Errors Gracefully

// ✅ Good - graceful error handling
try {
  await AuthManager.login(credentials);
} catch (error) {
  // Error automatically handled by AuthErrorHandler
  console.log('Login failed, error handled');
}

// ❌ Bad - no error handling
await AuthManager.login(credentials);  // Uncaught errors!

2. Provide User Feedback

// ✅ Good - clear user feedback
document.addEventListener('auth:error:validation', (e) => {
  showValidationErrors(e.detail.errors);
  showNotification('Please fix the errors and try again', 'error');
});

// ❌ Bad - silent failures
// User doesn't know what went wrong

3. Log Errors Appropriately

// ✅ Good - structured error logging
document.addEventListener('auth:error', (e) => {
  const { error, context } = e.detail;

  logger.error({
    type: error.type,
    message: error.message,
    context: context,
    timestamp: new Date().toISOString()
  });
});

// ❌ Bad - no logging
// Cannot debug production issues

4. Implement Retry with Limits

// ✅ Good - retry with exponential backoff and limit
{
  maxRetries: 3,
  retryBackoff: 'exponential',
  retryDelay: 1000
}

// ❌ Bad - unlimited retries
{
  maxRetries: Infinity  // Never stops trying!
}

Common Pitfalls

1. Not Handling All Error Types

// ❌ Bad - only handles unauthorized
document.addEventListener('auth:error:unauthorized', () => {
  redirectToLogin();
});
// What about network errors? Validation errors?

// ✅ Good - handle all error types
document.addEventListener('auth:error', (e) => {
  const { error } = e.detail;

  switch (error.type) {
    case 'UNAUTHORIZED':
      redirectToLogin();
      break;
    case 'NETWORK_ERROR':
      showOfflineMode();
      break;
    case 'VALIDATION_ERROR':
      showValidationErrors(error.errors);
      break;
    default:
      showGenericError(error.message);
  }
});

2. Swallowing Errors

// ❌ Bad - error disappears
try {
  await AuthManager.login(credentials);
} catch (error) {
  // Do nothing - error is lost
}

// ✅ Good - at least log the error
try {
  await AuthManager.login(credentials);
} catch (error) {
  console.error('Login failed:', error);
  // Let AuthErrorHandler handle it
  throw error;
}

3. Retry Without Backoff

// ❌ Bad - retry immediately forever
{
  retryBackoff: 'fixed',
  retryDelay: 0,
  maxRetries: 999
}
// This hammers the server!

// ✅ Good - exponential backoff with limit
{
  retryBackoff: 'exponential',
  retryDelay: 1000,
  maxRetries: 3
}