Search docs

Jump between documentation pages.

Errors & problem+json

DaloyJS errors are first-class. Every thrown HttpError serializes to RFC 9457 problem+json with a stable type URI, a request-id, and the appropriate Content-Type.

Built-in error classes

ts
import {
  BadRequestError,            // 400
  ValidationError,            // 422
  UnauthorizedError,          // 401
  ForbiddenError,             // 403
  NotFoundError,              // 404
  MethodNotAllowedError,      // 405 + Allow header
  PayloadTooLargeError,       // 413
  UnsupportedMediaTypeError,  // 415
  RequestTimeoutError,        // 408
  TooManyRequestsError,       // 429 + Retry-After
  InternalError,              // 500 (detail redacted in production)
} from "@daloyjs/core";

Throwing in a handler

ts
import { NotFoundError } from "@daloyjs/core";

app.route({
  method: "GET",
  path: "/users/:id",
  operationId: "getUser",
  responses: { 200: { description: "ok" }, 404: { description: "missing" } },
  handler: async ({ params }) => {
    const user = await db.find(params.id);
    if (!user) throw new NotFoundError(`user ${params.id} not found`);
    return { status: 200, body: user };
  },
});

Wire format

The request id is returned to the client in two places: the x-request-id response header, and (per RFC 9457 §3.1) the problem document's instance field as a urn:request:<uuid> URN. There is no top-level requestId property — clients should read the header or parse the URN from instance.

json
HTTP/1.1 404 Not Found
content-type: application/problem+json
x-request-id: c9aa8e1c-7a6e-4f1e-9f44-c2e5d2c4a431

{
  "type": "https://daloyjs.dev/errors/not-found",
  "title": "Not Found",
  "status": 404,
  "detail": "user 42 not found",
  "instance": "urn:request:c9aa8e1c-7a6e-4f1e-9f44-c2e5d2c4a431"
}

Production redaction

When NODE_ENV=production, DaloyJS strips the detailfield on any 5xx response so internal stack traces and SQL fragments don't leak to clients. The full error is still emitted to your logger via the onError hook.

Custom error classes

ts
import { HttpError } from "@daloyjs/core";

export class QuotaExceededError extends HttpError {
  constructor(resource: string) {
    super(429, {
      title: "Quota exceeded",
      type: "https://api.example.com/errors/quota-exceeded",
      detail: `Quota exceeded for ${resource}`,
    });
  }
}

Custom onError

ts
app.use({
  onError: async (error, ctx) => {
    logger.error({ err: error, requestId: ctx?.requestId }, "request failed");
    // return a Response to override; otherwise DaloyJS serializes problem+json
  },
});