or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced-features.mdcore-framework.mderror-handling.mdevent-handling.mdhandlers-middleware.mdindex.mdrequest-processing.mdresponse-handling.mdruntime-adapters.mdweb-utilities.md
tile.json

error-handling.mddocs/

Error Handling

HTTP error handling with status codes, custom error data, structured error responses, and comprehensive error management utilities.

Capabilities

HTTPError Class

The main error class for HTTP-specific errors with status codes and metadata.

/**
 * HTTP error class with status code and additional metadata
 */
class HTTPError extends Error {
  /**
   * HTTP status code
   */
  status: number;
  
  /**
   * HTTP status text
   */
  statusText: string | undefined;
  
  /**
   * Additional HTTP headers to send with error
   */
  headers: Headers | undefined;
  
  /**
   * Original error that caused this HTTPError
   */
  cause: unknown | undefined;
  
  /**
   * Custom data associated with the error
   */
  data: any | undefined;
  
  /**
   * JSON body properties for error response
   */
  body: Record<string, unknown> | undefined;
  
  /**
   * Flag indicating if error was handled
   */
  unhandled: boolean | undefined;
  
  /**
   * Create HTTPError with message and optional details
   * @param message - Error message
   * @param details - Optional error details
   */
  constructor(message: string, details?: ErrorDetails);
  
  /**
   * Create HTTPError with error details object
   * @param details - Error details including status, message, etc.
   */
  constructor(details: ErrorDetails);
  
  /**
   * Serialize error to JSON for response
   * @returns JSON representation of error
   */
  toJSON(): ErrorBody;
  
  /**
   * Check if input is an HTTPError instance
   * @param input - Value to check
   * @returns True if input is HTTPError
   */
  static isError(input: any): input is HTTPError;
  
  /**
   * Create HTTPError with specific status code
   * @param status - HTTP status code
   * @param statusText - Optional status text
   * @param details - Optional additional details
   * @returns HTTPError instance
   */
  static status(status: number, statusText?: string, details?: ErrorDetails): HTTPError;
}

Usage Examples:

import { HTTPError } from "h3";

// Basic error creation
const basicHandler = defineHandler((event) => {
  const { id } = getRouterParams(event);
  
  if (!id) {
    throw new HTTPError("ID parameter is required", { status: 400 });
  }
  
  const user = findUser(id);
  if (!user) {
    throw HTTPError.status(404, "Not Found", {
      data: { resource: "user", id }
    });
  }
  
  return user;
});

// Error with custom data
const validationHandler = defineHandler(async (event) => {
  const body = await readBody(event);
  
  const errors = validateUser(body);
  if (errors.length > 0) {
    throw new HTTPError({
      status: 422,
      statusText: "Validation Error",
      message: "Invalid user data",
      data: { errors, input: body }
    });
  }
  
  return createUser(body);
});

// Error with headers
const authHandler = defineHandler((event) => {
  const token = getHeader(event, "authorization");
  
  if (!token) {
    throw new HTTPError("Authentication required", {
      status: 401,
      headers: new Headers({
        "WWW-Authenticate": "Bearer realm=\"api\""
      })
    });
  }
  
  return { authenticated: true };
});

// Error with cause
const externalHandler = defineHandler(async (event) => {
  try {
    const data = await fetchExternalAPI();
    return data;
  } catch (originalError) {
    throw new HTTPError("External service unavailable", {
      status: 503,
      cause: originalError,
      data: { service: "external-api" }
    });
  }
});

Status Code Helpers

Quick helpers for common HTTP status codes.

// Common status code patterns
const statusHelpers = {
  badRequest: (message?: string, data?: any) => 
    HTTPError.status(400, "Bad Request", { message, data }),
  
  unauthorized: (message?: string) => 
    HTTPError.status(401, "Unauthorized", { message }),
  
  forbidden: (message?: string) => 
    HTTPError.status(403, "Forbidden", { message }),
  
  notFound: (resource?: string, id?: string) => 
    HTTPError.status(404, "Not Found", { 
      message: resource ? `${resource} not found` : "Resource not found",
      data: { resource, id }
    }),
  
  methodNotAllowed: (allowed: string[]) => 
    HTTPError.status(405, "Method Not Allowed", {
      headers: new Headers({ "Allow": allowed.join(", ") })
    }),
  
  conflict: (message?: string, data?: any) => 
    HTTPError.status(409, "Conflict", { message, data }),
  
  unprocessable: (errors: any[]) => 
    HTTPError.status(422, "Unprocessable Entity", { 
      message: "Validation failed",
      data: { errors }
    }),
  
  tooManyRequests: (retryAfter?: number) => 
    HTTPError.status(429, "Too Many Requests", {
      headers: retryAfter ? new Headers({ "Retry-After": retryAfter.toString() }) : undefined
    }),
  
  internalServerError: (message?: string) => 
    HTTPError.status(500, "Internal Server Error", { message }),
  
  notImplemented: (feature?: string) => 
    HTTPError.status(501, "Not Implemented", { 
      message: feature ? `${feature} not implemented` : "Not implemented"
    }),
  
  serviceUnavailable: (retryAfter?: number) => 
    HTTPError.status(503, "Service Unavailable", {
      headers: retryAfter ? new Headers({ "Retry-After": retryAfter.toString() }) : undefined
    })
};

Usage Examples:

// Use status helpers
const resourceHandler = defineHandler((event) => {
  const { id } = getRouterParams(event);
  
  if (!id) {
    throw statusHelpers.badRequest("ID parameter required");
  }
  
  const resource = findResource(id);
  if (!resource) {
    throw statusHelpers.notFound("resource", id);
  }
  
  return resource;
});

// Validation errors
const validationHandler = defineHandler(async (event) => {
  const body = await readBody(event);
  const errors = validate(body);
  
  if (errors.length > 0) {
    throw statusHelpers.unprocessable(errors);
  }
  
  return { valid: true };
});

// Rate limiting
const rateLimitHandler = defineHandler((event) => {
  const ip = getRequestIP(event);
  
  if (isRateLimited(ip)) {
    const resetTime = getRateLimitReset(ip);
    throw statusHelpers.tooManyRequests(resetTime);
  }
  
  return { allowed: true };
});

Error Response Handling

Handle and format error responses consistently.

/**
 * Global error handler pattern
 */
const errorHandler = onError((error, event) => {
  console.error(`Error in ${event.url}:`, error);
  
  // Handle HTTPError instances
  if (HTTPError.isError(error)) {
    const response = error.toJSON();
    
    // Add debug info in development
    if (process.env.NODE_ENV === "development") {
      response.stack = error.stack;
      response.cause = error.cause;
    }
    
    return response;
  }
  
  // Handle other errors
  return {
    message: "Internal Server Error",
    status: 500,
    ...(process.env.NODE_ENV === "development" && {
      stack: error.stack,
      name: error.name
    })
  };
});

Usage Examples:

// Apply global error handler
const app = new H3({
  onError: errorHandler
});

// Custom error formatting
const formatError = (error: HTTPError, event: H3Event) => {
  const baseResponse = error.toJSON();
  
  return {
    ...baseResponse,
    timestamp: new Date().toISOString(),
    path: event.url.pathname,
    method: event.req.method,
    requestId: event.context.requestId
  };
};

// Route-specific error handling
const protectedHandler = defineHandler({
  handler: async (event) => {
    // Protected logic
    return { data: "protected" };
  },
  onError: [
    (error, event) => {
      if (HTTPError.isError(error) && error.status === 401) {
        // Custom unauthorized handling
        return {
          error: "Authentication required",
          loginUrl: "/login",
          timestamp: Date.now()
        };
      }
    }
  ]
});

Error Types

Error Interface Definitions

/**
 * Error details for HTTPError construction
 */
interface ErrorDetails {
  /**
   * HTTP status code
   */
  status?: number;
  
  /**
   * HTTP status text
   */
  statusText?: string;
  
  /**
   * Additional headers to send
   */
  headers?: HeadersInit;
  
  /**
   * Original error cause
   */
  cause?: unknown;
  
  /**
   * Custom data for error
   */
  data?: any;
  
  /**
   * Custom error message
   */
  message?: string;
}

/**
 * Error input type (union of message and details)
 */
type ErrorInput<DataT = any> = string | (ErrorDetails & { data?: DataT });

/**
 * JSON error response body
 */
interface ErrorBody<DataT = any> {
  /**
   * Error message
   */
  message: string;
  
  /**
   * HTTP status code
   */
  status?: number;
  
  /**
   * HTTP status text
   */
  statusText?: string;
  
  /**
   * Custom error data
   */
  data?: DataT;
}

Advanced Error Handling

Error Recovery

Implement error recovery and fallback mechanisms.

// Error recovery pattern
const resilientHandler = defineHandler(async (event) => {
  try {
    // Primary operation
    return await primaryService.getData();
  } catch (error) {
    console.warn("Primary service failed, trying fallback:", error);
    
    try {
      // Fallback operation
      return await fallbackService.getData();
    } catch (fallbackError) {
      // Both failed, return error with context
      throw new HTTPError("Service unavailable", {
        status: 503,
        data: {
          primaryError: error.message,
          fallbackError: fallbackError.message
        }
      });
    }
  }
});

Error Aggregation

Collect and aggregate multiple errors.

// Error aggregation pattern
const batchHandler = defineHandler(async (event) => {
  const requests = await readBody(event);
  const results = [];
  const errors = [];
  
  for (const [index, request] of requests.entries()) {
    try {
      const result = await processRequest(request);
      results.push({ index, result });
    } catch (error) {
      errors.push({ 
        index, 
        error: HTTPError.isError(error) ? error.toJSON() : { message: error.message }
      });
    }
  }
  
  if (errors.length > 0) {
    throw new HTTPError("Batch processing failed", {
      status: 207, // Multi-Status
      data: { results, errors }
    });
  }
  
  return { results };
});

Structured Error Logging

Implement structured error logging for better debugging.

// Structured error logging
const structuredErrorHandler = onError((error, event) => {
  const errorLog = {
    timestamp: new Date().toISOString(),
    level: "error",
    message: error.message,
    status: HTTPError.isError(error) ? error.status : 500,
    method: event.req.method,
    url: event.url.toString(),
    userAgent: event.req.headers.get("user-agent"),
    ip: getRequestIP(event),
    requestId: event.context.requestId,
    stack: error.stack,
    ...(HTTPError.isError(error) && {
      data: error.data,
      cause: error.cause
    })
  };
  
  // Log to your preferred logging service
  logger.error(errorLog);
  
  // Return sanitized error for client
  if (HTTPError.isError(error)) {
    return error.toJSON();
  }
  
  return {
    message: "Internal Server Error",
    status: 500,
    requestId: event.context.requestId
  };
});

Error Monitoring

Integrate with error monitoring services.

// Error monitoring integration
const monitoringErrorHandler = onError(async (error, event) => {
  // Send to monitoring service
  await errorMonitoringService.captureException(error, {
    tags: {
      method: event.req.method,
      status: HTTPError.isError(error) ? error.status : 500
    },
    extra: {
      url: event.url.toString(),
      headers: Object.fromEntries(event.req.headers.entries()),
      context: event.context
    }
  });
  
  // Continue with normal error handling
  if (HTTPError.isError(error)) {
    return error.toJSON();
  }
  
  return { message: "Internal Server Error", status: 500 };
});