Files
vip-coordinator/docs/ERROR_HANDLING.md
kyle 440884666d
Some checks failed
CI/CD Pipeline / Backend Tests (push) Has been cancelled
CI/CD Pipeline / Frontend Tests (push) Has been cancelled
CI/CD Pipeline / Build Docker Images (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
docs: Organize documentation into structured folders
Organized documentation into cleaner structure:

Root directory (user-facing):
- README.md - Main documentation
- CLAUDE.md - AI context (referenced by system)
- QUICKSTART.md - Quick start guide

docs/ (technical documentation):
- CASL_AUTHORIZATION.md - Authorization guide
- ERROR_HANDLING.md - Error handling patterns
- REQUIREMENTS.md - Project requirements

docs/deployment/ (production deployment):
- HTTPS_SETUP.md - SSL/TLS setup
- PRODUCTION_ENVIRONMENT_TEMPLATE.md - Env vars template
- PRODUCTION_VERIFICATION_CHECKLIST.md - Deployment checklist

Removed:
- DOCKER_TROUBLESHOOTING.md - Outdated (referenced Google OAuth, old domain)

Updated references:
- Fixed links to moved files in CASL_AUTHORIZATION.md
- Fixed links to moved files in ERROR_HANDLING.md
- Removed reference to deleted BUILD_STATUS.md in QUICKSTART.md

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-31 17:13:47 +01:00

8.9 KiB

Error Handling Guide

This document describes the error handling patterns implemented in the VIP Coordinator application.

Backend Error Handling

Global Exception Filters

The backend uses two global exception filters for consistent error responses:

1. HttpExceptionFilter

Handles all HTTP exceptions (4xx, 5xx errors) and formats them consistently.

Features:

  • Standardized error response format
  • Automatic logging with appropriate levels (error/warn/log)
  • Request context logging (params, query, body)
  • Sensitive data redaction (passwords, tokens, API keys)

Error Response Format:

{
  "statusCode": 400,
  "timestamp": "2026-01-25T10:30:00.000Z",
  "path": "/api/v1/events",
  "method": "POST",
  "message": "Validation failed",
  "error": "Bad Request",
  "details": { ... }
}

2. AllExceptionsFilter

Catch-all filter for unhandled errors (database errors, runtime exceptions, etc.).

Features:

  • Catches all non-HTTP exceptions
  • Prevents server crashes from unhandled errors
  • Logs full stack traces
  • Returns 500 Internal Server Error to client
  • In development: includes stack trace in response

Using Exceptions in Controllers/Services

Example:

import { BadRequestException, NotFoundException } from '@nestjs/common';

// Simple error
throw new NotFoundException('VIP not found');

// Error with details
throw new BadRequestException({
  message: 'Driver has conflicting events',
  conflicts: [
    { id: '123', title: 'Airport Pickup', startTime: '...' }
  ]
});

Logging Best Practices

import { Logger } from '@nestjs/common';

export class MyService {
  private readonly logger = new Logger(MyService.name);

  async doSomething() {
    this.logger.log('Starting operation...');
    this.logger.warn('Conflict detected');
    this.logger.error('Operation failed', error.stack);
  }
}

Frontend Error Handling

ErrorHandler Utility

The ErrorHandler class provides centralized error handling for the frontend.

Location: frontend/src/lib/errorHandler.ts

Quick Usage

import { handleError } from '@/lib/errorHandler';

try {
  await api.createVIP(data);
  toast.success('VIP created successfully');
} catch (error) {
  handleError('VIP Creation', error, 'Failed to create VIP');
}

Advanced Usage

Extract Error Message Only

import { ErrorHandler } from '@/lib/errorHandler';

try {
  await api.get('/vips');
} catch (error) {
  const message = ErrorHandler.getMessage(error, 'Failed to load VIPs');
  setErrorMessage(message);
}

Show Toast Only

ErrorHandler.showError(error, 'Operation failed');

Log Only (No Toast)

ErrorHandler.log('VIP Fetch', error, { vipId: '123' });

Check Error Type

try {
  await api.updateVIP(id, data);
} catch (error) {
  if (ErrorHandler.isAuthError(error)) {
    // Redirect to login
    navigate('/login');
  } else if (ErrorHandler.isConflict(error)) {
    // Show conflict resolution UI
    const conflicts = ErrorHandler.getConflicts(error);
    setConflicts(conflicts);
  } else {
    ErrorHandler.showError(error);
  }
}

Available Methods

Method Description Example
getMessage(error, fallback?) Extract user-friendly message const msg = ErrorHandler.getMessage(error)
showError(error, fallback?) Show error toast ErrorHandler.showError(error)
log(context, error, info?) Log to console with context ErrorHandler.log('API', error, { id })
handle(context, error, fallback?, info?) Log + toast ErrorHandler.handle('Save', error)
isAuthError(error) Check if 401/403 if (ErrorHandler.isAuthError(error))
isConflict(error) Check if 409 if (ErrorHandler.isConflict(error))
isValidationError(error) Check if 400 if (ErrorHandler.isValidationError(error))
getConflicts(error) Extract conflicts array const conflicts = ErrorHandler.getConflicts(error)

API Client Logging

The axios client automatically logs all requests and responses in development mode.

Console output format:

[API] → POST /api/v1/vips { data: {...} }
[API] ← 201 POST /api/v1/vips { data: {...} }
[API] ✖ 400 POST /api/v1/events { status: 400, data: {...} }

Error types logged:

  • 401: Authentication required
  • 403: Permission denied
  • 404: Resource not found
  • 409: Conflict (with conflict details)
  • 500+: Server error

Migration Guide

Before (Old Pattern)

try {
  await api.createVIP(data);
  toast.success('VIP created');
} catch (error: any) {
  console.error('Failed to create VIP:', error);
  toast.error(error.response?.data?.message || 'Failed to create VIP');
}

After (New Pattern)

import { handleError } from '@/lib/errorHandler';

try {
  await api.createVIP(data);
  toast.success('VIP created');
} catch (error) {
  handleError('VIP Creation', error, 'Failed to create VIP');
}

Benefits:

  • Consistent error messages across app
  • Automatic console logging with context
  • Handles all error types (Axios, Error, unknown)
  • Type-safe error checking
  • Less code duplication

Common Error Scenarios

1. Network Error (Backend Down)

Backend logs: None (backend not reachable) Frontend logs: [API] ✖ Network error - no response received User sees: Toast: "Network error. Please check your connection."

2. Authentication Error (401)

Backend logs: [WARN] [GET] /api/v1/vips - 401 - Unauthorized Frontend logs: [API] ✖ 401 GET /api/v1/vips + warning User sees: Toast: "Authentication required. Please log in again."

3. Permission Error (403)

Backend logs: [WARN] [POST] /api/v1/users - 403 - Forbidden resource Frontend logs: [API] ✖ 403 POST /api/v1/users + warning User sees: Toast: "You do not have permission to perform this action."

4. Validation Error (400)

Backend logs: [WARN] [POST] /api/v1/events - 400 - Validation failed Frontend logs: [API] ✖ 400 POST /api/v1/events + response data User sees: Toast: "Validation failed: email must be a valid email"

5. Conflict Error (409)

Backend logs: [WARN] [POST] /api/v1/events - 409 - Driver has conflicting events Frontend logs: [API] ✖ 409 POST /api/v1/events + conflicts array User sees: Toast: "A conflict occurred. Please check for overlapping schedules."

6. Server Error (500)

Backend logs: [ERROR] [GET] /api/v1/vips - 500 - Internal server error + stack trace Frontend logs: [API] ✖ 500 GET /api/v1/vips + error message User sees: Toast: "Server error. Please try again later."

Best Practices

DO

// Use handleError for simple cases
try {
  await api.post('/vips', data);
  toast.success('Success');
} catch (error) {
  handleError('VIP Creation', error);
}

// Use ErrorHandler methods for complex cases
try {
  await api.updateEvent(id, data);
  toast.success('Event updated');
} catch (error) {
  if (ErrorHandler.isConflict(error)) {
    // Show conflict resolution dialog
    showConflictDialog(ErrorHandler.getConflicts(error));
  } else {
    ErrorHandler.showError(error);
  }
}

// Log errors with context
ErrorHandler.log('Event Update', error, { eventId: id, changes: data });

// Use structured logging in backend
this.logger.log(`Created VIP: ${vip.name} (ID: ${vip.id})`);
this.logger.error(`Failed to create VIP: ${error.message}`, error.stack);

DON'T

// Don't use generic error messages
catch (error) {
  toast.error('An error occurred'); // Too vague!
}

// Don't ignore errors
catch (error) {
  console.log(error); // Use console.error and proper context
}

// Don't manually extract error messages
catch (error: any) {
  const msg = error.response?.data?.message || 'Failed'; // Use ErrorHandler.getMessage()
  toast.error(msg);
}

// Don't log sensitive data
this.logger.log(`User login: ${email} with password ${password}`); // Never log passwords!

Debugging Tips

Enable Verbose Logging

Backend: Set LOG_LEVEL=debug in .env

Frontend: Development mode already has verbose logging enabled

Check Error Context

Backend logs include:

  • Timestamp
  • HTTP method
  • URL path
  • Status code
  • Request params/query/body
  • User email (if authenticated)

Frontend logs include:

  • Timestamp
  • Context (feature area)
  • HTTP method
  • URL
  • Request/response data
  • Error type

Common Issues

"Network error" but backend is running:

  • Check CORS configuration
  • Verify API_URL in .env
  • Check browser DevTools Network tab

"Authentication required" after login:

  • Check if token is stored in localStorage
  • Verify Auth0 configuration
  • Check if user is approved in database

Validation errors don't show field names:

  • Backend DTO needs proper validation decorators
  • Use class-validator's @IsString(), @IsEmail(), etc.

Last Updated: 2026-01-25 See also: CLAUDE.md for general project documentation