Skip to content

Conversation

@CodeMaverick-143
Copy link

@CodeMaverick-143 CodeMaverick-143 commented Nov 13, 2025

This PR introduces a complete React Error Boundary system to fix the critical issue: “Missing Error Boundaries”.
Right now, any unhandled React error can crash the entire UI — this update fully closes that gap.


What’s Added

1. Global Error Boundary

  • Wraps the entire React application
  • Prevents full-app crashes
  • Provides fallback UI + reload option

2. Route-Level Error Boundaries

  • Each route is isolated
  • A crash in one page won’t nuke the whole app
  • Custom RouteErrorBoundary component included

3. Component-Level Error Boundaries

  • HOC-based (withErrorBoundary)
  • Can wrap critical components (WebSocket, DebateRoom, Matchmaking, etc.)
  • Graceful fallbacks + reset behavior

4. Error Fallback UIs

  • Friendly UI messages
  • “Retry”, “Go Home”, and “Back” actions
  • Detailed errors only in dev mode

5. Error Logging System

  • ErrorLogger to capture and store errors
  • Debounced logging + offline mode support
  • Prepares ground for future Sentry/LogRocket integration

Why This Matters

  • Prevents white-screen-of-death crashes
  • Improves reliability for debate rooms, live match components, WebSocket flows
  • Allows safe error recovery without losing user state
  • Smooth UX instead of sudden breakdowns

What I Tested

  • Route error triggers → fallback UI works
  • Component error → isolated crash only
  • Global crash → App recovers
  • Offline error logging → queued
  • Navigation after errors → stable

Files Added / Modified

  • ErrorBoundary.tsx
  • RouteErrorBoundary.tsx
  • ErrorFallback.tsx
  • ErrorLogger.ts
  • withErrorBoundary.tsx
  • index.ts (exports)
  • Updated App.tsx + main.tsx

Summary by CodeRabbit

  • New Features

    • Added comprehensive error handling throughout the application to display user-friendly error screens when issues occur.
    • Error screens now provide action buttons such as "Try Again," "Go Home," and "Go Back" to help users recover.
    • In development mode, error details are displayed to aid with debugging.
  • Bug Fixes

    • Improved application stability by capturing and handling errors at multiple levels.

@coderabbitai
Copy link

coderabbitai bot commented Nov 13, 2025

Walkthrough

This PR implements a comprehensive error boundary system for the React frontend. It introduces reusable error boundary components with centralized error logging, moves React type declarations from devDependencies to dependencies, wraps all application routes with error protection, and integrates global error handling at the app initialization level.

Changes

Cohort / File(s) Summary
Dependency Management
frontend/package.json
Moves @types/react and @types/react-dom from devDependencies to dependencies; no other functional changes
Error Boundary Core
frontend/src/components/ErrorBoundary/types.ts, ErrorBoundary.tsx, ErrorFallback.tsx, ErrorLogger.ts
Introduces error state interfaces, lifecycle-based ErrorBoundary component with reset logic, three fallback UI components (DefaultErrorFallback, RouteErrorFallback, ComponentErrorFallback), and a singleton ErrorLogger with localStorage persistence and remote error reporting
Error Boundary Utilities
frontend/src/components/ErrorBoundary/RouteErrorBoundary.tsx, withErrorBoundary.tsx, index.ts
Creates RouteErrorBoundary wrapper component, three higher-order components (withErrorBoundary, withRouteErrorBoundary, withComponentErrorBoundary), and barrel export module for centralized public API
App Integration
frontend/src/App.tsx, frontend/src/main.tsx
Wraps all application routes (public and protected) with RouteErrorBoundary; wraps entire app with ErrorBoundary at root initialization level
Build Artifacts
frontend/tsconfig.app.tsbuildinfo, frontend/tsconfig.node.tsbuildinfo
Deletes TypeScript incremental build cache files; no logic changes

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Route as Route/Component
    participant EB as ErrorBoundary
    participant Logger as ErrorLogger
    participant LocalStorage as localStorage
    participant RemoteAPI as /api/errors

    User->>Route: Navigate/Interact
    alt Error Occurs
        Route->>EB: Throws Error
        EB->>EB: getDerivedStateFromError()
        EB->>EB: componentDidCatch()
        EB->>Logger: logError(error, componentStack)
        Logger->>Logger: buildErrorDetails()
        Logger->>LocalStorage: storeErrorLocally()
        LocalStorage-->>Logger: Save (last 50)
        Logger->>RemoteAPI: sendToRemoteLogger()
        alt Online
            RemoteAPI-->>Logger: Success/Failure
        else Offline
            Logger->>Logger: Queue error
            Note over Logger: Flush on reconnect
        end
        EB->>EB: Render Fallback UI
        EB-->>User: Show Error + Retry Button
        User->>EB: Click Retry
        EB->>EB: resetErrorBoundary()
        EB->>Route: Re-render children
    else No Error
        Route-->>EB: Render normally
        EB-->>User: Display content
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • ErrorLogger.ts – Requires careful review of singleton pattern, localStorage handling, async remote logging logic, and offline queue management
  • ErrorBoundary.tsx – Lifecycle methods, state management, reset logic with resetKeys and prop-change detection need verification
  • App.tsx – Route wrapping strategy across public/protected routes should be validated for complete coverage
  • Error state propagation – Verify that error boundaries at different levels (global, route, component) don't create unexpected cascading behavior
  • Type definitions – Ensure ErrorBoundaryProps and ErrorFallbackProps contracts align with all consuming components

Suggested reviewers

  • bhavik-mangla

Poem

🐰 Errors now wrapped in boundaries so tight,
Logging and catching with all of their might,
Routes protected from top down to ground,
When things go sideways, recovery's found!
Fallbacks and retries, resilience takes flight!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a comprehensive error boundary system with global, route, and component-level coverage, which matches the core functionality introduced across multiple new files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (3)
frontend/src/App.tsx (1)

54-232: Consider reducing nesting with a route configuration pattern.

While wrapping each route with RouteErrorBoundary provides good isolation, the current implementation creates significant nesting (3-4 levels deep) and repetition. Consider extracting route definitions into a configuration object and mapping them to reduce boilerplate.

Example pattern:

// Route configuration
const routes = [
  { path: '/', element: Home, protected: false },
  { path: '/auth', element: Authentication, protected: false },
  { path: '/startDebate', element: StartDebate, protected: true },
  // ... other routes
];

// Helper to wrap routes
const renderRoute = (config) => (
  <Route
    key={config.path}
    path={config.path}
    element={
      <RouteErrorBoundary>
        <config.element />
      </RouteErrorBoundary>
    }
  />
);

This approach would reduce code duplication and improve maintainability while preserving the same error boundary isolation.

frontend/src/components/ErrorBoundary/ErrorFallback.tsx (1)

55-88: Consider adding development error details for consistency.

Unlike DefaultErrorFallback, this component doesn't show error details in development mode. For debugging route-specific errors, consider adding a similar development-only details section.

Add after the buttons div (around line 84):

{process.env.NODE_ENV === 'development' && error && (
  <details className="mt-4 text-left">
    <summary className="cursor-pointer text-xs text-muted-foreground hover:text-foreground flex items-center gap-2">
      <Bug className="h-3 w-3" />
      Error Details (Development)
    </summary>
    <pre className="mt-2 text-xs bg-muted p-2 rounded overflow-auto max-h-32">
      {error.message}
      {error.stack && `\n\n${error.stack}`}
    </pre>
  </details>
)}
frontend/src/components/ErrorBoundary/withErrorBoundary.tsx (1)

43-48: Redundant fallback specification.

The withComponentErrorBoundary HOC explicitly sets fallback: ComponentErrorFallback, which is already the default value (line 18). This can be simplified.

 export const withComponentErrorBoundary = <P extends object>(
   Component: React.ComponentType<P>
 ) => withErrorBoundary(Component, {
-  fallback: ComponentErrorFallback,
   displayName: `withComponentErrorBoundary(${Component.displayName || Component.name})`,
 });

Alternatively, keep it explicit for clarity if that's the team's preference.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 26aff7c and d534caf.

⛔ Files ignored due to path filters (1)
  • frontend/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • frontend/package.json (1 hunks)
  • frontend/src/App.tsx (2 hunks)
  • frontend/src/components/ErrorBoundary/ErrorBoundary.tsx (1 hunks)
  • frontend/src/components/ErrorBoundary/ErrorFallback.tsx (1 hunks)
  • frontend/src/components/ErrorBoundary/ErrorLogger.ts (1 hunks)
  • frontend/src/components/ErrorBoundary/RouteErrorBoundary.tsx (1 hunks)
  • frontend/src/components/ErrorBoundary/index.ts (1 hunks)
  • frontend/src/components/ErrorBoundary/types.ts (1 hunks)
  • frontend/src/components/ErrorBoundary/withErrorBoundary.tsx (1 hunks)
  • frontend/src/main.tsx (1 hunks)
  • frontend/tsconfig.app.tsbuildinfo (0 hunks)
  • frontend/tsconfig.node.tsbuildinfo (0 hunks)
💤 Files with no reviewable changes (2)
  • frontend/tsconfig.node.tsbuildinfo
  • frontend/tsconfig.app.tsbuildinfo
🧰 Additional context used
🧬 Code graph analysis (6)
frontend/src/components/ErrorBoundary/ErrorLogger.ts (1)
frontend/src/components/ErrorBoundary/types.ts (1)
  • ErrorDetails (27-35)
frontend/src/components/ErrorBoundary/RouteErrorBoundary.tsx (3)
frontend/src/components/ErrorBoundary/types.ts (1)
  • ErrorBoundaryProps (13-19)
frontend/src/components/ErrorBoundary/ErrorFallback.tsx (1)
  • RouteErrorFallback (55-88)
frontend/src/components/ErrorBoundary/index.ts (1)
  • RouteErrorFallback (6-6)
frontend/src/components/ErrorBoundary/withErrorBoundary.tsx (2)
frontend/src/components/ErrorBoundary/types.ts (1)
  • ErrorBoundaryProps (13-19)
frontend/src/components/ErrorBoundary/ErrorFallback.tsx (1)
  • ComponentErrorFallback (90-122)
frontend/src/components/ErrorBoundary/ErrorFallback.tsx (2)
frontend/src/components/ErrorBoundary/index.ts (3)
  • DefaultErrorFallback (5-5)
  • RouteErrorFallback (6-6)
  • ComponentErrorFallback (7-7)
frontend/src/components/ErrorBoundary/types.ts (1)
  • ErrorFallbackProps (21-25)
frontend/src/components/ErrorBoundary/ErrorBoundary.tsx (2)
frontend/src/components/ErrorBoundary/types.ts (3)
  • ErrorBoundaryProps (13-19)
  • ErrorBoundaryState (7-11)
  • ErrorInfo (3-5)
frontend/src/components/ErrorBoundary/ErrorFallback.tsx (1)
  • DefaultErrorFallback (4-53)
frontend/src/App.tsx (2)
frontend/src/components/ErrorBoundary/RouteErrorBoundary.tsx (1)
  • RouteErrorBoundary (10-20)
frontend/src/Pages/ViewDebate.tsx (1)
  • ViewDebate (31-522)
🔇 Additional comments (20)
frontend/package.json (1)

26-26: Move @types/react and @types/react-dom back to devDependencies.

TypeScript type packages (@types/*) should remain in devDependencies, not dependencies. These packages are used only during development for type checking; TypeScript removes all type information during compilation, and the runtime output contains no type metadata. Bundlers do not include these packages in the production bundle.

Unless the error boundary components have a specific production runtime requirement for these types (which is highly unlikely), reverting this change aligns with TypeScript ecosystem best practices.

Can you clarify the rationale for moving these to production dependencies? If there's no specific runtime requirement, they should be reverted to devDependencies.

Also applies to: 28-28

frontend/src/main.tsx (1)

7-16: LGTM! Clean global error boundary integration.

The ErrorBoundary wrapping at the root level is correctly positioned inside StrictMode and outside BrowserRouter, ensuring all routing and app errors are caught before the app crashes.

frontend/src/components/ErrorBoundary/RouteErrorBoundary.tsx (1)

10-20: LGTM! Clean wrapper implementation.

The component correctly composes ErrorBoundary with sensible defaults and proper prop forwarding.

frontend/src/components/ErrorBoundary/ErrorBoundary.tsx (6)

10-20: LGTM! Proper initialization.

Constructor correctly initializes state and obtains the ErrorLogger singleton instance.


22-27: LGTM! Standard error boundary pattern.

getDerivedStateFromError correctly updates state to trigger fallback rendering.


29-42: LGTM! Proper error handling.

The component correctly logs errors, updates state, and invokes the optional error handler callback.


65-69: LGTM! Proper cleanup.

Timeout is correctly cleared on unmount to prevent memory leaks.


71-83: LGTM! Clean reset implementation.

The method properly clears pending timeouts and resets error state.


85-100: LGTM! Standard error boundary render pattern.

Correctly renders either the fallback UI with appropriate props or the children when no error exists.

frontend/src/components/ErrorBoundary/ErrorFallback.tsx (3)

29-35: Verify the hard navigation is intentional.

Line 30 uses window.location.href = '/' which triggers a full page reload, clearing all React state and losing React Router's client-side navigation. For a global error boundary, this might be intentional to ensure a clean reset after critical errors.

If preserving app state is desired, consider using React Router's navigation:

import { useNavigate } from 'react-router-dom';

// Convert to a function component or use a wrapper
export function DefaultErrorFallback({ error, resetError }: ErrorFallbackProps) {
  const navigate = useNavigate();
  
  // ...
  <button onClick={() => navigate('/')}>
    Go Home
  </button>
}

However, for critical errors, a hard reload may be the safer choice.


38-49: LGTM! Appropriate development-only debugging.

Error details are correctly shown only in development mode, providing useful debugging information without exposing sensitive details in production.


90-122: LGTM! Appropriate compact fallback for component errors.

The minimal design is well-suited for inline component errors, and the development debug info provides essential information without overwhelming the UI.

frontend/src/components/ErrorBoundary/index.ts (1)

1-14: LGTM! Clean barrel export pattern.

The index file is well-structured and provides a clear public API for the ErrorBoundary feature. All exports are properly organized and follow standard barrel export conventions.

frontend/src/components/ErrorBoundary/types.ts (1)

1-35: LGTM! Well-designed type definitions.

The type definitions are comprehensive and appropriate for the error boundary system. All interfaces are correctly structured with proper typing.

frontend/src/components/ErrorBoundary/ErrorLogger.ts (4)

3-18: LGTM! Singleton pattern correctly implemented.

The singleton pattern is properly implemented with a private constructor and online/offline event listeners. The automatic queue flushing when the network comes back online is a good UX touch.


27-56: LGTM! Comprehensive error logging.

The logError method properly handles error details construction, development logging, local storage, and conditional remote logging based on network status.


58-66: LGTM! Proper local storage with size limit.

Good use of slice(-50) to cap the stored errors and prevent unbounded growth. The try-catch ensures graceful degradation if localStorage is unavailable.


99-114: LGTM! Proper error handling for localStorage operations.

Both methods include appropriate try-catch blocks with fallback behavior, ensuring the logger remains resilient even if localStorage operations fail.

frontend/src/components/ErrorBoundary/withErrorBoundary.tsx (2)

6-11: LGTM! Appropriate type definitions.

The type definitions properly extend ErrorBoundaryProps and accommodate both regular and forwardRef components.


23-26: Type safety concern with as any cast is valid, but alternatives need verification.

The as any cast on line 26 is correctly identified as reducing type safety. However, this HOC differs from other components in the codebase—it wraps a union component type (React.ComponentType<P> | React.ForwardRefExoticComponent<P>) rather than a concrete type. This union constraint complicates typing and is likely why the as any cast exists here uniquely.

Before applying the suggested alternatives (e.g., React.ComponentPropsWithRef<typeof Component>), verify that they work with the union component type constraint on line 11. The suggested refactorings may not resolve the underlying TypeScript limitation without additional type restructuring.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant