API Error Handling Implementation Guide
Table of Contents
- Overview
- Error Response Format
- Error Types
- Error Code Taxonomy
- Client Retry Logic
- Error Logging
- User-Friendly Messages
- Implementation Examples
Overview
This guide establishes standardized error handling practices for all APIs in the vacation rental platform. It is based on RFC 7807: Problem Details for HTTP APIs, which provides a machine-readable format for specifying errors in HTTP API responses.
Goals
- Consistency: Uniform error format across all microservices
- Debuggability: Rich error context for developers
- User Experience: Clear, actionable error messages for end users
- Automation: Machine-readable errors for client-side error handling
- Observability: Comprehensive error logging and tracking
Key Principles
- Use standard HTTP status codes correctly
- Return RFC 7807-compliant problem details
- Include correlation IDs for tracing
- Separate technical details from user-facing messages
- Never expose sensitive data in error responses
- Log errors with appropriate severity levels
RFC 7807 Benefits
- Standardization: Industry-standard format (application/problem+json)
- Extensibility: Custom fields for domain-specific context
- Tool Support: Wide ecosystem support for parsing and handling
- Documentation: Self-documenting error types via URI references
Error Response Format
Base Problem Details Structure
All error responses must follow this RFC 7807 format:
{
  "type": "https://api.tvl.com/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Unit name is required and must be between 1-255 characters",
  "instance": "/api/v1/units/123",
  "traceId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-10-25T14:30:00Z"
}
Field Definitions
| Field | Required | Type | Description | 
|---|---|---|---|
| type | Yes | URI | URI reference identifying the problem type. Must be dereferenceable documentation | 
| title | Yes | String | Short, human-readable summary. Should not change from occurrence to occurrence | 
| status | Yes | Integer | HTTP status code (duplicated for convenience) | 
| detail | Yes | String | Human-readable explanation specific to this occurrence | 
| instance | Yes | URI | URI reference identifying the specific occurrence. Should include resource path | 
| traceId | Yes | UUID | Correlation ID for distributed tracing (OpenTelemetry trace ID) | 
| timestamp | Yes | ISO8601 | When the error occurred | 
Extended Fields for Validation Errors
For validation errors (400), include detailed field-level errors:
{
  "type": "https://api.tvl.com/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed on 3 fields",
  "instance": "/api/v1/units",
  "traceId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-10-25T14:30:00Z",
  "errors": [
    {
      "field": "name",
      "code": "REQUIRED",
      "message": "Unit name is required",
      "rejectedValue": null
    },
    {
      "field": "maxOccupancy",
      "code": "MIN_VALUE",
      "message": "Max occupancy must be at least 1",
      "rejectedValue": 0
    },
    {
      "field": "email",
      "code": "INVALID_FORMAT",
      "message": "Email must be a valid email address",
      "rejectedValue": "not-an-email"
    }
  ]
}
Extended Fields for Business Logic Errors
For domain-specific errors (409, 422), include business context:
{
  "type": "https://api.tvl.com/errors/booking-conflict",
  "title": "Booking Conflict",
  "status": 409,
  "detail": "Unit is already booked for the requested dates",
  "instance": "/api/v1/bookings",
  "traceId": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2025-10-25T14:30:00Z",
  "errorCode": "BOOKING_CONFLICT",
  "conflictingBookingId": "bkg_789",
  "requestedCheckIn": "2025-11-01",
  "requestedCheckOut": "2025-11-05",
  "availableFrom": "2025-11-06"
}
Content-Type Header
Always return error responses with:
Content-Type: application/problem+json; charset=utf-8
Error Types
1. Validation Errors (400 Bad Request)
When to use: Client sent syntactically or semantically invalid request data.
{
  "type": "https://api.tvl.com/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "detail": "Request validation failed",
  "errorCode": "VALIDATION_ERROR"
}
Common scenarios:
- Missing required fields
- Invalid data types or formats
- Values outside allowed ranges
- Invalid enum values
- Malformed JSON
Retryable: No (fix request and retry)
2. Authentication Errors (401 Unauthorized)
When to use: Authentication is required but missing or invalid.
{
  "type": "https://api.tvl.com/errors/authentication-required",
  "title": "Authentication Required",
  "status": 401,
  "detail": "Valid authentication credentials are required to access this resource",
  "errorCode": "AUTH_TOKEN_MISSING",
  "wwwAuthenticate": "Bearer realm=\"TVL API\""
}
Common scenarios:
- Missing Authorization header
- Expired JWT token
- Invalid token signature
- Revoked credentials
Retryable: No (obtain valid credentials first)
Variants:
- AUTH_TOKEN_MISSING: No token provided
- AUTH_TOKEN_EXPIRED: Token expired
- AUTH_TOKEN_INVALID: Token signature invalid
- AUTH_TOKEN_REVOKED: Token has been revoked
3. Authorization Errors (403 Forbidden)
When to use: Authenticated user lacks permission for the requested operation.
{
  "type": "https://api.tvl.com/errors/authorization-error",
  "title": "Forbidden",
  "status": 403,
  "detail": "You do not have permission to delete this unit",
  "errorCode": "INSUFFICIENT_PERMISSIONS",
  "requiredPermission": "units:delete",
  "resourceId": "unit_123"
}
Common scenarios:
- Insufficient role/permissions
- Cross-tenant access attempt
- Resource ownership violation
- Policy-based access denial
Retryable: No (requires permission change)
4. Not Found Errors (404 Not Found)
When to use: Requested resource does not exist.
{
  "type": "https://api.tvl.com/errors/resource-not-found",
  "title": "Resource Not Found",
  "status": 404,
  "detail": "Unit with ID 'unit_123' was not found",
  "errorCode": "RESOURCE_NOT_FOUND",
  "resourceType": "Unit",
  "resourceId": "unit_123"
}
Common scenarios:
- Invalid resource ID
- Deleted resource
- Typo in URL path
- Resource not visible due to tenant isolation
Retryable: No (unless resource is being created asynchronously)
5. Conflict Errors (409 Conflict)
When to use: Request conflicts with current state of the resource.
{
  "type": "https://api.tvl.com/errors/booking-conflict",
  "title": "Booking Conflict",
  "status": 409,
  "detail": "Unit is already booked for the requested dates",
  "errorCode": "BOOKING_DATE_CONFLICT",
  "conflictingBookingId": "bkg_789",
  "requestedCheckIn": "2025-11-01",
  "requestedCheckOut": "2025-11-05"
}
Common scenarios:
- Double booking attempts
- Duplicate resource creation
- Optimistic locking failures
- State transition violations
Retryable: Sometimes (after resolving conflict)
6. Unprocessable Entity (422 Unprocessable Entity)
When to use: Request is well-formed but semantically invalid.
{
  "type": "https://api.tvl.com/errors/business-rule-violation",
  "title": "Business Rule Violation",
  "status": 422,
  "detail": "Check-out date must be after check-in date",
  "errorCode": "INVALID_DATE_RANGE",
  "checkInDate": "2025-11-05",
  "checkOutDate": "2025-11-03"
}
Common scenarios:
- Business rule violations
- Invalid state transitions
- Logical inconsistencies
- Cross-field validation failures
Retryable: No (fix business logic and retry)
7. Rate Limit Errors (429 Too Many Requests)
When to use: Client has exceeded rate limits.
{
  "type": "https://api.tvl.com/errors/rate-limit-exceeded",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "You have exceeded the rate limit of 100 requests per minute",
  "errorCode": "RATE_LIMIT_EXCEEDED",
  "limit": 100,
  "remaining": 0,
  "resetAt": "2025-10-25T14:31:00Z"
}
Headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1730302260
Retry-After: 60
Retryable: Yes (after reset time)
8. Server Errors (500 Internal Server Error)
When to use: Unexpected server-side error occurred.
{
  "type": "https://api.tvl.com/errors/internal-server-error",
  "title": "Internal Server Error",
  "status": 500,
  "detail": "An unexpected error occurred. Please try again later.",
  "errorCode": "INTERNAL_SERVER_ERROR",
  "traceId": "550e8400-e29b-41d4-a716-446655440000"
}
Important: Never expose stack traces, database errors, or internal details to clients.
Retryable: Yes (with exponential backoff)
9. Service Unavailable (503 Service Unavailable)
When to use: Service is temporarily unavailable.
{
  "type": "https://api.tvl.com/errors/service-unavailable",
  "title": "Service Unavailable",
  "status": 503,
  "detail": "Service is temporarily unavailable. Please try again later.",
  "errorCode": "SERVICE_UNAVAILABLE",
  "retryAfter": 120
}
Headers:
Retry-After: 120
Common scenarios:
- Deployment in progress
- Database maintenance
- Circuit breaker open
- Dependency unavailable
Retryable: Yes (after retry-after period)
Error Code Taxonomy
Error Code Structure
Error codes follow a hierarchical naming convention:
{DOMAIN}_{ENTITY}_{ERROR_TYPE}
Examples:
- BOOKING_DATE_CONFLICT
- PRICING_CALCULATION_FAILED
- PAYMENT_AUTHORIZATION_DECLINED
- UNIT_AVAILABILITY_UNAVAILABLE
Domain-Specific Error Codes
Identity & Tenancy Domain
| Code | Status | Description | 
|---|---|---|
| IDENTITY_USER_NOT_FOUND | 404 | User does not exist | 
| IDENTITY_EMAIL_ALREADY_EXISTS | 409 | Email already registered | 
| IDENTITY_TENANT_NOT_FOUND | 404 | Tenant does not exist | 
| IDENTITY_TENANT_SUSPENDED | 403 | Tenant account suspended | 
| IDENTITY_PASSWORD_TOO_WEAK | 422 | Password does not meet requirements | 
Authorization Domain
| Code | Status | Description | 
|---|---|---|
| AUTH_INSUFFICIENT_PERMISSIONS | 403 | Missing required permission | 
| AUTH_ROLE_NOT_FOUND | 404 | Role does not exist | 
| AUTH_POLICY_VIOLATION | 403 | Access policy denied request | 
| AUTH_CROSS_TENANT_ACCESS | 403 | Cross-tenant access not allowed | 
Supply Domain
| Code | Status | Description | 
|---|---|---|
| SUPPLY_UNIT_NOT_FOUND | 404 | Unit does not exist | 
| SUPPLY_SPACE_NOT_FOUND | 404 | Space does not exist | 
| SUPPLY_UNIT_INACTIVE | 422 | Unit is not active | 
| SUPPLY_MAX_OCCUPANCY_INVALID | 400 | Invalid occupancy value | 
Availability Domain
| Code | Status | Description | 
|---|---|---|
| AVAILABILITY_DATE_UNAVAILABLE | 409 | Dates not available | 
| AVAILABILITY_BLOCKED | 409 | Dates are blocked | 
| AVAILABILITY_MIN_STAY_VIOLATION | 422 | Minimum stay requirement not met | 
| AVAILABILITY_MAX_STAY_VIOLATION | 422 | Maximum stay exceeded | 
Pricing Domain
| Code | Status | Description | 
|---|---|---|
| PRICING_CALCULATION_FAILED | 500 | Pricing calculation error | 
| PRICING_RULE_NOT_FOUND | 404 | Pricing rule does not exist | 
| PRICING_NO_BASE_RATE | 422 | No base rate configured | 
| PRICING_DISCOUNT_INVALID | 400 | Invalid discount code | 
Booking Domain
| Code | Status | Description | 
|---|---|---|
| BOOKING_NOT_FOUND | 404 | Booking does not exist | 
| BOOKING_DATE_CONFLICT | 409 | Dates conflict with existing booking | 
| BOOKING_QUOTE_EXPIRED | 422 | Quote has expired | 
| BOOKING_ALREADY_CONFIRMED | 409 | Booking already confirmed | 
| BOOKING_CANCELLATION_NOT_ALLOWED | 422 | Cancellation policy violation | 
Payment Domain
| Code | Status | Description | 
|---|---|---|
| PAYMENT_AUTHORIZATION_FAILED | 422 | Payment authorization failed | 
| PAYMENT_CARD_DECLINED | 422 | Card declined by issuer | 
| PAYMENT_INSUFFICIENT_FUNDS | 422 | Insufficient funds | 
| PAYMENT_REFUND_FAILED | 500 | Refund processing failed | 
| PAYMENT_PROVIDER_UNAVAILABLE | 503 | Payment provider unavailable | 
Error Code Registry
Maintain a centralized error code registry in code:
// src/common/errors/error-codes.ts
export const ErrorCodes = {
  // Validation
  VALIDATION_ERROR: 'VALIDATION_ERROR',
  // Identity & Tenancy
  IDENTITY_USER_NOT_FOUND: 'IDENTITY_USER_NOT_FOUND',
  IDENTITY_EMAIL_ALREADY_EXISTS: 'IDENTITY_EMAIL_ALREADY_EXISTS',
  // Booking
  BOOKING_DATE_CONFLICT: 'BOOKING_DATE_CONFLICT',
  BOOKING_QUOTE_EXPIRED: 'BOOKING_QUOTE_EXPIRED',
  // Add all error codes here
} as const;
export type ErrorCode = typeof ErrorCodes[keyof typeof ErrorCodes];
Client Retry Logic
Retryability Classification
| Status Code | Retryable | Strategy | 
|---|---|---|
| 400 | No | Fix request | 
| 401 | No | Re-authenticate | 
| 403 | No | Check permissions | 
| 404 | No | Verify resource exists | 
| 409 | Conditional | Resolve conflict first | 
| 422 | No | Fix business logic | 
| 429 | Yes | Respect Retry-After | 
| 500 | Yes | Exponential backoff | 
| 502 | Yes | Exponential backoff | 
| 503 | Yes | Respect Retry-After | 
| 504 | Yes | Exponential backoff | 
Exponential Backoff Strategy
Implement exponential backoff with jitter for retryable errors:
async function retryWithBackoff<T>(
  fn: () => Promise<T>,
  options: {
    maxRetries: number;
    baseDelayMs: number;
    maxDelayMs: number;
  }
): Promise<T> {
  const { maxRetries, baseDelayMs, maxDelayMs } = options;
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (attempt === maxRetries || !isRetryable(error)) {
        throw error;
      }
      // Calculate delay with exponential backoff and jitter
      const exponentialDelay = baseDelayMs * Math.pow(2, attempt);
      const jitter = Math.random() * exponentialDelay * 0.1;
      const delay = Math.min(exponentialDelay + jitter, maxDelayMs);
      await sleep(delay);
    }
  }
}
function isRetryable(error: ApiError): boolean {
  const retryableStatusCodes = [429, 500, 502, 503, 504];
  return retryableStatusCodes.includes(error.status);
}
Rate Limit Handling
Respect Retry-After header for 429 responses:
async function handleRateLimit(error: ApiError): Promise<void> {
  if (error.status === 429) {
    const retryAfter = error.headers['retry-after'];
    if (retryAfter) {
      // Retry-After can be seconds or HTTP date
      const delayMs = isNaN(Number(retryAfter))
        ? new Date(retryAfter).getTime() - Date.now()
        : Number(retryAfter) * 1000;
      await sleep(delayMs);
    } else {
      // Fallback to exponential backoff
      await sleep(60000); // 60 seconds
    }
  }
}
Circuit Breaker Pattern
Implement circuit breaker to prevent cascading failures:
class CircuitBreaker {
  private failureCount = 0;
  private lastFailureTime = 0;
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
  constructor(
    private failureThreshold: number = 5,
    private resetTimeoutMs: number = 60000
  ) {}
  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailureTime > this.resetTimeoutMs) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }
    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }
  private onSuccess(): void {
    this.failureCount = 0;
    this.state = 'CLOSED';
  }
  private onFailure(): void {
    this.failureCount++;
    this.lastFailureTime = Date.now();
    if (this.failureCount >= this.failureThreshold) {
      this.state = 'OPEN';
    }
  }
}
Error Logging
What to Log
Always log:
- Trace ID (correlation ID)
- Timestamp
- HTTP method and path
- Status code
- Error code
- User ID (if authenticated)
- Tenant ID (if multi-tenant)
- Request ID
Conditionally log:
- Request body (exclude PII)
- Response body (sanitize)
- Stack trace (5xx errors only)
- Database query (if applicable)
Never log:
- Passwords or credentials
- Full credit card numbers
- Social security numbers
- Personal health information
- API keys or tokens
Log Levels
| Error Type | Log Level | Alert | 
|---|---|---|
| 400-level (client errors) | INFO/WARN | No | 
| 401, 403 (auth errors) | WARN | After threshold | 
| 404 (not found) | INFO | No | 
| 429 (rate limit) | WARN | After threshold | 
| 500 (server error) | ERROR | Yes | 
| 503 (unavailable) | WARN | After threshold | 
Structured Logging Format
Use structured JSON logging for machine readability:
{
  "timestamp": "2025-10-25T14:30:00Z",
  "level": "ERROR",
  "message": "Internal server error occurred",
  "traceId": "550e8400-e29b-41d4-a716-446655440000",
  "spanId": "7f9c8d6e",
  "service": "booking-service",
  "environment": "production",
  "error": {
    "type": "https://api.tvl.com/errors/internal-server-error",
    "code": "INTERNAL_SERVER_ERROR",
    "status": 500,
    "message": "Database connection timeout"
  },
  "request": {
    "method": "POST",
    "path": "/api/v1/bookings",
    "userId": "usr_123",
    "tenantId": "tnt_456",
    "ip": "192.168.1.1",
    "userAgent": "Mozilla/5.0..."
  },
  "context": {
    "bookingId": "bkg_789",
    "unitId": "unit_123"
  },
  "stack": "Error: Database connection timeout\n  at ..."
}
PII Considerations
Implement PII scrubbing before logging:
const PII_FIELDS = ['password', 'ssn', 'creditCard', 'token'];
function sanitizeForLogging(obj: any): any {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  const sanitized = { ...obj };
  for (const key in sanitized) {
    if (PII_FIELDS.some(field => key.toLowerCase().includes(field))) {
      sanitized[key] = '[REDACTED]';
    } else if (typeof sanitized[key] === 'object') {
      sanitized[key] = sanitizeForLogging(sanitized[key]);
    }
  }
  return sanitized;
}
Error Tracking Integration
Integrate with error tracking services (e.g., Sentry, Rollbar):
import * as Sentry from '@sentry/node';
function reportError(error: Error, context: any): void {
  Sentry.captureException(error, {
    level: 'error',
    tags: {
      service: 'booking-service',
      errorCode: context.errorCode
    },
    extra: sanitizeForLogging(context)
  });
}
User-Friendly Messages
Technical vs. User Messages
Separate technical details from user-facing messages:
{
  "type": "https://api.tvl.com/errors/booking-conflict",
  "title": "Booking Conflict",
  "status": 409,
  "detail": "Unit is already booked for the requested dates",
  "userMessage": "This property is unavailable for your selected dates. Please choose different dates or view similar properties.",
  "technicalDetails": {
    "conflictingBookingId": "bkg_789",
    "databaseQuery": "SELECT * FROM bookings WHERE ..."
  }
}
Localization
Support multiple languages via Accept-Language header:
{
  "type": "https://api.tvl.com/errors/validation-error",
  "title": "Erreur de validation",
  "status": 400,
  "detail": "Le nom de l'unité est requis",
  "locale": "fr-FR",
  "errors": [
    {
      "field": "name",
      "code": "REQUIRED",
      "message": "Le nom de l'unité est requis"
    }
  ]
}
Localization implementation:
const ERROR_MESSAGES = {
  'en-US': {
    BOOKING_DATE_CONFLICT: 'This property is unavailable for your selected dates',
    VALIDATION_ERROR: 'Please check your input and try again'
  },
  'fr-FR': {
    BOOKING_DATE_CONFLICT: 'Cette propriété n\'est pas disponible pour vos dates sélectionnées',
    VALIDATION_ERROR: 'Veuillez vérifier votre saisie et réessayer'
  }
};
function getLocalizedMessage(
  errorCode: string,
  locale: string = 'en-US'
): string {
  return ERROR_MESSAGES[locale]?.[errorCode] || ERROR_MESSAGES['en-US'][errorCode];
}
Message Guidelines
Do:
- Use simple, clear language
- Explain what went wrong
- Suggest how to fix it
- Be empathetic and helpful
Don't:
- Blame the user
- Use technical jargon
- Expose internal system details
- Be vague or unhelpful
Examples:
| Bad | Good | 
|---|---|
| "Invalid input" | "Please enter a valid email address" | 
| "Error 500" | "Something went wrong. Please try again later" | 
| "FK constraint violation" | "This property cannot be deleted because it has active bookings" | 
| "Unauthorized" | "You don't have permission to perform this action. Contact your administrator for access" | 
Implementation Examples
Express.js Middleware
import { Request, Response, NextFunction } from 'express';
interface ProblemDetails {
  type: string;
  title: string;
  status: number;
  detail: string;
  instance: string;
  traceId: string;
  timestamp: string;
  [key: string]: any;
}
class ApiError extends Error {
  constructor(
    public status: number,
    public errorCode: string,
    public detail: string,
    public extensions?: Record<string, any>
  ) {
    super(detail);
    this.name = 'ApiError';
  }
}
function errorHandler(
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction
): void {
  const traceId = req.headers['x-trace-id'] as string || generateTraceId();
  if (err instanceof ApiError) {
    const problem: ProblemDetails = {
      type: `https://api.tvl.com/errors/${err.errorCode.toLowerCase().replace(/_/g, '-')}`,
      title: err.errorCode.replace(/_/g, ' '),
      status: err.status,
      detail: err.detail,
      instance: req.path,
      traceId,
      timestamp: new Date().toISOString(),
      ...err.extensions
    };
    logError(problem, req);
    res
      .status(err.status)
      .type('application/problem+json')
      .json(problem);
  } else {
    // Unexpected error
    const problem: ProblemDetails = {
      type: 'https://api.tvl.com/errors/internal-server-error',
      title: 'Internal Server Error',
      status: 500,
      detail: 'An unexpected error occurred',
      instance: req.path,
      traceId,
      timestamp: new Date().toISOString()
    };
    logError({ ...problem, stack: err.stack }, req);
    res
      .status(500)
      .type('application/problem+json')
      .json(problem);
  }
}
FastAPI (Python) Exception Handler
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from datetime import datetime
import uuid
class ApiError(Exception):
    def __init__(
        self,
        status: int,
        error_code: str,
        detail: str,
        extensions: dict = None
    ):
        self.status = status
        self.error_code = error_code
        self.detail = detail
        self.extensions = extensions or {}
@app.exception_handler(ApiError)
async def api_error_handler(request: Request, exc: ApiError):
    trace_id = request.headers.get('x-trace-id', str(uuid.uuid4()))
    problem = {
        'type': f'https://api.tvl.com/errors/{exc.error_code.lower().replace("_", "-")}',
        'title': exc.error_code.replace('_', ' ').title(),
        'status': exc.status,
        'detail': exc.detail,
        'instance': request.url.path,
        'traceId': trace_id,
        'timestamp': datetime.utcnow().isoformat() + 'Z',
        **exc.extensions
    }
    log_error(problem, request)
    return JSONResponse(
        status_code=exc.status,
        content=problem,
        media_type='application/problem+json'
    )
Summary
This API error handling guide establishes a consistent, RFC 7807-compliant approach to error responses across all services. By following these standards, we ensure:
- Developer Experience: Clear, actionable error messages with rich context
- Client Resilience: Proper retry logic and circuit breaker patterns
- Observability: Comprehensive error logging with PII protection
- User Experience: Localized, user-friendly error messages
All services must implement these error handling patterns to maintain consistency and reliability across the platform.