Error Handling Pattern

Centralized error handling across backend, frontend, and the shared API contract.

Backend Error Handler

backend/src/middleware/errorHandler.ts is the centralized error handler in the Middleware Stack:

  • Catches all unhandled errors from route handlers and services
  • Maps custom AppError subclasses to HTTP status codes
  • Handles Zod ValidationError separately (400 with field details)
  • Generates unique error IDs for tracking
  • Logs full context (request UUID, path, method, client IP)
  • Sends to Sentry with sensitive data scrubbing
  • Returns safe error messages in production (no stack traces)

Custom Error Classes

Defined in backend/src/lib/errors.ts:

ClassHTTP StatusUse Case
AppError500Base class
ValidationError400Input validation failures
DatabaseError500DB operation failures
NotFoundError404Entity not found
UnauthorizedError401Authentication failure
ForbiddenError403Authorization failure

Frontend Error Handling

ComponentFilePurpose
Error boundaryfrontend/src/components/Catches React render errors
Error parserfrontend/src/utils/errorParser.tsExtracts user-friendly messages from API errors
Stale import reloaderFrontend error boundaryDetects post-deploy chunk loading failures, triggers reload

API Error Contract

Shared types in shared/api-types.ts:

interface ApiError {
  message: string;
  code: string;
  errorId?: string;
}
 
type ApiResponse<T> = { data: T } | { error: ApiError };

The isApiError() type guard is available for safe error narrowing.

Timeout Strategy

Request timeout middleware (backend/src/middleware/timeout.ts) enforces a 55-second limit, which is 5 seconds shorter than the 60-second Fly.io load balancer timeout. This ensures the app returns a proper error response before the load balancer kills the connection.

See Also