Files
thrilltrack-explorer/docs/ERROR_BOUNDARIES.md
gpt-engineer-app[bot] ee09e3652c feat: Add error boundaries
2025-11-03 14:51:39 +00:00

11 KiB

Error Boundaries Implementation (P0 #5)

Status: Complete

Priority: P0 - Critical (Stability)
Effort: 8-12 hours
Date Completed: 2025-11-03


Overview

Error boundaries are React components that catch JavaScript errors in their child component tree, log the errors, and display a fallback UI instead of crashing the entire application.

Before P0 #5: Only 1 error boundary (ModerationErrorBoundary)
After P0 #5: 5 specialized error boundaries covering all critical sections


Error Boundary Architecture

1. RouteErrorBoundary (Top-Level)

Purpose: Last line of defense, wraps all routes
Location: src/components/error/RouteErrorBoundary.tsx
Used in: src/App.tsx - wraps <Routes>

Features:

  • Catches route-level errors before they crash the app
  • Full-screen error UI with reload/home options
  • Critical severity logging
  • Minimal UI to ensure maximum stability

Usage:

<RouteErrorBoundary>
  <Routes>
    {/* All routes */}
  </Routes>
</RouteErrorBoundary>

2. AdminErrorBoundary

Purpose: Protects admin panel sections
Location: src/components/error/AdminErrorBoundary.tsx
Used in: Admin routes (/admin/*)

Features:

  • Admin-specific error UI with shield icon
  • "Back to Dashboard" recovery option
  • High-priority error logging
  • Section-aware error context

Usage:

<Route 
  path="/admin/users" 
  element={
    <AdminErrorBoundary section="User Management">
      <AdminUsers />
    </AdminErrorBoundary>
  } 
/>

Protected Sections:

  • Dashboard (/admin)
  • Moderation Queue (/admin/moderation)
  • Reports (/admin/reports)
  • System Log (/admin/system-log)
  • User Management (/admin/users)
  • Blog Management (/admin/blog)
  • Settings (/admin/settings)
  • Contact Management (/admin/contact)
  • Email Settings (/admin/email-settings)

3. EntityErrorBoundary

Purpose: Protects entity detail pages
Location: src/components/error/EntityErrorBoundary.tsx
Used in: Park, Ride, Manufacturer, Designer, Operator, Owner detail routes

Features:

  • Entity-aware error messages
  • "Back to List" navigation option
  • Helpful troubleshooting suggestions
  • Graceful degradation

Usage:

<Route 
  path="/parks/:slug" 
  element={
    <EntityErrorBoundary entityType="park">
      <ParkDetail />
    </EntityErrorBoundary>
  } 
/>

Supported Entity Types:

  • park → Back to /parks
  • ride → Back to /rides
  • manufacturer → Back to /manufacturers
  • designer → Back to /designers
  • operator → Back to /operators
  • owner → Back to /owners

Protected Routes:

  • Park Detail (/parks/:slug)
  • Park Rides (/parks/:parkSlug/rides)
  • Ride Detail (/parks/:parkSlug/rides/:rideSlug)
  • Manufacturer Detail (/manufacturers/:slug)
  • Manufacturer Rides (/manufacturers/:manufacturerSlug/rides)
  • Manufacturer Models (/manufacturers/:manufacturerSlug/models)
  • Model Detail (/manufacturers/:manufacturerSlug/models/:modelSlug)
  • Model Rides (/manufacturers/:manufacturerSlug/models/:modelSlug/rides)
  • Designer Detail (/designers/:slug)
  • Designer Rides (/designers/:designerSlug/rides)
  • Owner Detail (/owners/:slug)
  • Owner Parks (/owners/:ownerSlug/parks)
  • Operator Detail (/operators/:slug)
  • Operator Parks (/operators/:operatorSlug/parks)

4. ErrorBoundary (Generic)

Purpose: General-purpose error boundary for any component
Location: src/components/error/ErrorBoundary.tsx

Features:

  • Context-aware error messages
  • Customizable fallback UI
  • Optional error callback
  • Retry and "Go Home" options

Usage:

import { ErrorBoundary } from '@/components/error';

<ErrorBoundary context="PhotoUpload">
  <PhotoUploadForm />
</ErrorBoundary>

// With custom fallback
<ErrorBoundary 
  context="ComplexChart"
  fallback={<p>Failed to load chart</p>}
  onError={(error, info) => {
    // Custom error handling
    analytics.track('chart_error', { error: error.message });
  }}
>
  <ComplexChart data={data} />
</ErrorBoundary>

5. ModerationErrorBoundary

Purpose: Protects individual moderation queue items
Location: src/components/error/ModerationErrorBoundary.tsx
Status: Pre-existing, retained

Features:

  • Item-level error isolation
  • Submission ID tracking
  • Copy error details functionality
  • Prevents one broken item from crashing the queue

Error Boundary Hierarchy

App
├── RouteErrorBoundary (TOP LEVEL - catches everything)
│   └── Routes
│       ├── Admin Routes
│       │   └── AdminErrorBoundary (per admin section)
│       │       └── AdminModeration
│       │           └── ModerationErrorBoundary (per queue item)
│       │
│       ├── Entity Detail Routes
│       │   └── EntityErrorBoundary (per entity page)
│       │       └── ParkDetail
│       │
│       └── Generic Routes
│           └── ErrorBoundary (optional, as needed)
│               └── ComplexComponent

Error Logging

All error boundaries use structured logging via logger.error():

logger.error('Component error caught by boundary', {
  context: 'PhotoUpload',
  error: error.message,
  stack: error.stack,
  componentStack: errorInfo.componentStack,
  url: window.location.href,
  userId: user?.id, // If available
});

Log Severity Levels:

  • RouteErrorBoundary: critical (app-level failure)
  • AdminErrorBoundary: high (admin functionality impacted)
  • EntityErrorBoundary: medium (user-facing page impacted)
  • ErrorBoundary: medium (component failure)
  • ModerationErrorBoundary: medium (queue item failure)

Recovery Options

User Recovery Actions

Each error boundary provides appropriate recovery options:

Boundary Actions Available
RouteErrorBoundary Reload Page, Go Home
AdminErrorBoundary Retry, Back to Dashboard, Copy Error
EntityErrorBoundary Try Again, Back to List, Home
ErrorBoundary Try Again, Go Home, Copy Details
ModerationErrorBoundary Retry, Copy Error Details

Developer Recovery

In development mode, error boundaries show additional debug information:

  • Full error stack trace
  • Component stack trace
  • Error message and context
  • One-click copy to clipboard

Testing Error Boundaries

Manual Testing

  1. Force a component error:
const BrokenComponent = () => {
  throw new Error('Test error boundary');
  return <div>This won't render</div>;
};

// Wrap in error boundary
<ErrorBoundary context="Test">
  <BrokenComponent />
</ErrorBoundary>
  1. Test recovery:
    • Click "Try Again" → Component should re-render
    • Click "Go Home" → Navigate to home page
    • Check logs for structured error data

Automated Testing

import { render } from '@testing-library/react';
import { ErrorBoundary } from '@/components/error';

const BrokenComponent = () => {
  throw new Error('Test error');
};

test('error boundary catches error and shows fallback', () => {
  const { getByText } = render(
    <ErrorBoundary context="Test">
      <BrokenComponent />
    </ErrorBoundary>
  );

  expect(getByText('Something Went Wrong')).toBeInTheDocument();
  expect(getByText('Test error')).toBeInTheDocument();
});

Best Practices

Do

  • Wrap lazy-loaded routes with error boundaries
  • Use specific error boundaries (Admin, Entity) when available
  • Provide context for better error messages
  • Log errors with structured data
  • Test error boundaries regularly
  • Use error boundaries for third-party components
  • Add error boundaries around:
    • Form submissions
    • Data fetching components
    • Complex visualizations
    • Photo uploads
    • Editor components

Don't

  • Don't catch errors in event handlers (use try/catch instead)
  • Don't use error boundaries for expected errors (validation, 404s)
  • Don't nest identical error boundaries
  • Don't log sensitive data in error messages
  • Don't render without any error boundary (always have at least RouteErrorBoundary)

Common Use Cases

1. Protect Heavy Components

import { ErrorBoundary } from '@/components/error';

<ErrorBoundary context="RichTextEditor">
  <MDXEditor content={content} />
</ErrorBoundary>

2. Protect Third-Party Libraries

<ErrorBoundary context="ChartLibrary">
  <RechartsLineChart data={data} />
</ErrorBoundary>

3. Protect User-Generated Content Rendering

<ErrorBoundary context="UserBio">
  <ReactMarkdown>{user.bio}</ReactMarkdown>
</ErrorBoundary>

4. Protect Form Sections

<ErrorBoundary context="ParkDetailsSection">
  <ParkDetailsForm />
</ErrorBoundary>
<ErrorBoundary context="ParkLocationSection">
  <ParkLocationForm />
</ErrorBoundary>

Integration with Monitoring (Future)

Error boundaries are designed to integrate with error tracking services:

// Future: Sentry integration
import * as Sentry from '@sentry/react';

componentDidCatch(error: Error, errorInfo: ErrorInfo) {
  // Automatically sent to Sentry
  Sentry.captureException(error, {
    contexts: {
      react: {
        componentStack: errorInfo.componentStack,
      },
    },
    tags: {
      errorBoundary: this.props.context,
    },
  });
}

Metrics

Coverage

Category Before P0 #5 After P0 #5 Status
Admin routes 0% 100% (9/9 routes) Complete
Entity detail routes 0% 100% (14/14 routes) Complete
Top-level routes 0% 100% Complete
Queue items 100% 100% Maintained

Impact

  • Before: Any component error could crash the entire app
  • After: Component errors are isolated and recoverable
  • User Experience: Users see helpful error messages with recovery options
  • Developer Experience: Better error logging with full context

  • P0 #2: Console Statement Prevention → docs/LOGGING_POLICY.md
  • P0 #4: Hardcoded Secrets Removal → (completed)
  • Error Handling Patterns → src/lib/errorHandler.ts
  • Logger Implementation → src/lib/logger.ts

Maintenance

Adding a New Error Boundary

  1. Identify the component/section that needs protection
  2. Choose appropriate error boundary type:
    • Admin section? → AdminErrorBoundary
    • Entity page? → EntityErrorBoundary
    • Generic component? → ErrorBoundary
  3. Wrap the component in the route definition or parent component
  4. Provide context for better error messages
  5. Test the error boundary manually

Updating Existing Boundaries

  • Keep error messages user-friendly
  • Don't expose stack traces in production
  • Ensure recovery actions work correctly
  • Update tests when changing boundaries

Summary

5 error boundary types covering all critical sections
100% admin route coverage (9/9 routes)
100% entity route coverage (14/14 routes)
Top-level protection via RouteErrorBoundary
User-friendly error UIs with recovery options
Structured error logging for debugging
Development mode debugging with stack traces

Result: Application is significantly more stable and resilient to component errors. Users will never see a blank screen due to a single component failure.