errors.md 2.4 KB

Error Handling

Overview

All server-side errors extend a common AppError base class that carries a machine-readable code, an HTTP statusCode, and optional structured details. Infrastructure errors (K8s, Git provider) are always wrapped before surfacing so internal details stay in logs, not in HTTP responses. Unexpected errors are caught by a global handleError hook.

The App.Error interface in app.d.ts defines the shape of errors available to +error.svelte pages — just code and message, both safe to display.

Error Hierarchy

Error (built-in)
├── AppError                  — base for all application errors; has code + statusCode
│   ├── NotFoundError         404  NOT_FOUND
│   ├── UnauthorizedError     401  UNAUTHORIZED
│   ├── ForbiddenError        403  FORBIDDEN
│   ├── ValidationError       400  VALIDATION_ERROR  (+ per-field details)
│   ├── ConflictError         409  CONFLICT
│   └── InfrastructureError   502  INFRASTRUCTURE_ERROR  (wraps K8s / Git provider failures)
└── ConfigError               —    not an HTTP error; fatal startup error

ValidationError carries a details.fields map of field name → error message for form validation. InfrastructureError wraps the original error as cause for logging but exposes only a sanitized message to clients.

ConfigError is thrown during server init when a required config key is absent. It is not an HTTP error — it causes the process to exit (unhandled rejection) rather than serving a degraded state.

Error Flow

In route load functions and actions

Throw typed AppError subclasses for domain errors, or use SvelteKit's error() helper for simple cases. handleError in hooks.server.ts maps both to the client.

In infrastructure modules

Wrap all external call failures with wrapInfraError(system, operation, cause) before re-throwing. Never let raw fetch or Kubernetes client errors propagate to route handlers.

In hooks.server.ts (handleError)

Catches every error that escapes a load/action/endpoint:

  • AppError → logged at warn, { code, message } returned to +error.svelte
  • anything else → logged at error with full stack, generic INTERNAL_ERROR returned to client

requestId from event.locals is included in every error log entry for correlation.

No internal details (stack traces, system messages, env var values) ever reach the client.