For the complete documentation index, see llms.txt. This page is also available as Markdown.

Error Handling Patterns

Comprehensive error handling patterns for building resilient Aurelia applications with graceful degradation and user-friendly feedback.

These patterns show you how to handle errors gracefully across different layers of your Aurelia application - from API calls and routing to form validation and global error boundaries.

1. Global error boundary with user feedback

Goal: Catch unhandled errors anywhere in your app, log them for debugging, and show users a friendly message instead of a blank screen.

Steps

  1. Create an error boundary service that tracks errors and provides recovery options:

    import { ILogger, resolve } from '@aurelia/kernel';
    import { observable } from '@aurelia/runtime';
    
    export interface AppError {
      message: string;
      stack?: string;
      timestamp: number;
      context?: string;
    }
    
    export class ErrorBoundaryService {
      @observable currentError: AppError | null = null;
      private errors: AppError[] = [];
      private logger = resolve(ILogger);
    
      captureError(error: Error, context?: string) {
        const appError: AppError = {
          message: error.message,
          stack: error.stack,
          timestamp: Date.now(),
          context
        };
    
        this.errors.push(appError);
        this.currentError = appError;
    
        this.logger.error(`[${context || 'Unknown'}] ${error.message}`, error);
    
        return appError;
      }
    
      clearError() {
        this.currentError = null;
      }
    
      getRecentErrors(count: number = 10) {
        return this.errors.slice(-count);
      }
    }
  2. Register a global error handler during app startup:

    import { Aurelia } from 'aurelia';
    import { ErrorBoundaryService } from './error-boundary-service';
    
    export async function main() {
      const au = Aurelia.app(MyApp);
      const errorBoundary = au.container.get(ErrorBoundaryService);
    
      window.addEventListener('error', (event) => {
        errorBoundary.captureError(event.error, 'Window');
        event.preventDefault();
      });
    
      window.addEventListener('unhandledrejection', (event) => {
        errorBoundary.captureError(
          new Error(event.reason?.message || 'Unhandled promise rejection'),
          'Promise'
        );
        event.preventDefault();
      });
    
      await au.start();
    }
  3. Display errors in your root component with recovery actions:

    import { resolve } from '@aurelia/kernel';
    import { ErrorBoundaryService } from './error-boundary-service';
    
    export class MyApp {
      private errorBoundary = resolve(ErrorBoundaryService);
    
      reload() {
        window.location.reload();
      }
    
      dismissError() {
        this.errorBoundary.clearError();
      }
    }
    <div class="error-banner" if.bind="errorBoundary.currentError">
      <div class="error-content">
        <h3>Something went wrong</h3>
        <p>${errorBoundary.currentError.message}</p>
        <div class="error-actions">
          <button click.trigger="dismissError()">Dismiss</button>
          <button click.trigger="reload()">Reload Page</button>
        </div>
      </div>
    </div>
    
    <au-viewport></au-viewport>

Checklist

  • Unhandled errors display in the banner instead of crashing the app

  • Console logs include full error details for debugging

  • Users can dismiss transient errors or reload for critical failures

  • Error history is available for support tickets

2. API error handling with retry and fallback

Goal: Handle API failures gracefully with automatic retries, fallback data, and clear user feedback about network issues.

Steps

  1. Create an API service wrapper with error handling:

  2. Use the service in components with loading and error states:

  3. Display appropriate UI for each state:

Checklist

  • Failed API calls retry automatically with exponential backoff

  • Components show loading state during fetch operations

  • Users see clear error messages when data fails to load

  • Fallback data prevents empty states from crashing the UI

  • Users can manually retry failed operations

3. Router navigation error handling

Goal: Handle route navigation failures, missing routes, and guard rejections with appropriate redirects and user feedback.

Steps

  1. Create a navigation error handler:

  2. Configure router with error handling:

  3. Create an error page with helpful actions:

Checklist

  • Failed navigation redirects to error page instead of blank screen

  • Authentication failures redirect to login with return URL

  • Users can retry failed navigation or return home

  • 404 routes show custom not-found page

  • Navigation errors are logged for debugging

4. Form validation errors with field-level feedback

Goal: Display validation errors inline next to form fields while preventing submission of invalid data.

Steps

  1. Create a form with validation rules and error display:

  2. Display validation errors inline with accessible markup:

Checklist

  • Validation errors appear inline next to each field

  • Users see errors on blur and as they type to fix them

  • Form submission is prevented when validation fails

  • Server-side errors merge with client-side validation

  • Submit button is disabled during submission

  • Error messages are accessible with ARIA attributes

5. Async operation error boundaries with timeout

Goal: Wrap async operations with timeouts and error handling to prevent indefinite loading states.

Steps

  1. Create a timeout utility with error handling:

  2. Use the timeout wrapper in components:

Checklist

  • Long-running operations timeout with clear error messages

  • Timeout errors are distinguishable from other failures

  • Users aren't stuck in infinite loading states

  • Errors are logged for monitoring

6. Optimistic updates with rollback on error

Goal: Update UI immediately for better UX, but revert changes if the server rejects the update.

Steps

  1. Implement optimistic updates with error recovery:

  2. Provide visual feedback during updates:

Checklist

  • UI updates immediately when users interact

  • Failed updates revert to previous state

  • Users see error notifications for failed operations

  • Original state is preserved for rollback

Error handling cheat sheet

Scenario
Pattern
Key Components

Global unhandled errors

Error boundary service + window event listeners

ErrorBoundaryService, window.addEventListener

API failures

Retry with exponential backoff

fetchWithErrorHandling, retry counter, delay

Navigation errors

Route guards + error pages

canLoad, loading, error routes

Form validation

Inline field errors + submit prevention

ValidationController, validation-errors

Async timeouts

Promise.race with timeout

withTimeout, TimeoutError

Optimistic updates

State snapshot + rollback

Save original state, try/catch with restore

Best practices

  1. Always show user feedback: Never fail silently - users should know when something goes wrong

  2. Provide recovery actions: Include "Retry", "Go Home", or "Dismiss" buttons

  3. Log errors for debugging: Use ErrorBoundaryService or similar to track errors

  4. Use appropriate error granularity: Global errors for critical failures, inline errors for forms

  5. Test error paths: Don't just test happy paths - simulate failures and verify recovery

  6. Avoid error cascades: Handle errors at the appropriate level to prevent bubbling

  7. Use loading states: Show when async operations are in progress

  8. Set reasonable timeouts: Don't let users wait indefinitely

  9. Make errors actionable: Tell users what they can do to fix the problem

See also

Last updated

Was this helpful?