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.
- 01handlerthrow new NotFoundError(...)any HttpError subclass
- 02frameworkMap to RFC 9457 fieldstype · title · status · detail
- 03productionRedact 5xx detailshown only when env is development or test
- 04wireSerialized responseapplication/problem+json + x-request-id
Built-in error classes
TooManyRequestsError and ServiceUnavailableError validate retryAfterSeconds the same way: anything other than a non-negative safe integer throws a RangeError at construction, so a NaN from a missing env var never becomes an invalid Retry-After header. RequestTimeoutError (the requestTimeoutMs deadline, see requestTimeoutStatus) is no-store for every status variant since 1.16.0, so a shared cache never keeps serving a 503/504 after the backend recovers. A bare new HttpError(status, …) gets a https://httpstatuses.io/<status> problem type; the built-in classes carry stable https://daloyjs.dev/errors/<slug> URIs.
Throwing in a handler
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.
Production redaction
DaloyJS strips the detail field on any 5xx response unless the environment is positively development or test, so internal error messages and SQL fragments don't leak to clients. The full error is still emitted to your logger and to the onError hook.
| Configuration | 5xx detail |
|---|---|
env: "development" or env: "test" | shown |
no env, NODE_ENV=development or test | shown |
no env, NODE_ENV unset, empty, or anything else | redacted |
env: "production", production: true, or NODE_ENV=production | redacted |
production: false with NODE_ENV unset | redacted |
An unset environment fails closed on purpose. Edge runtimes such as Cloudflare Workers and Deno Deploy have no NODE_ENV, and treating that as development would send exception messages to clients. daloy dev sets NODE_ENV=development for its child process when you haven't set it, so local development still shows the detail. Errors you serialize yourself (for example a response body built from err.message in a catch block) are not redacted. Throw them instead, or throw an HttpError with a safe detail.
Custom error classes
The built-in classes above all set a https://daloyjs.dev/errors/<slug> type. A plain HttpError thrown without a type falls back to https://httpstatuses.io/<status>. First-party middleware that answers 503 (loadShedding(), concurrencyLimit(), requestTimeoutStatus: 503) sets its own daloyjs.dev type, so the fallback only applies to errors you construct yourself. Set type when clients key on it, or use a built-in class:
Service unavailable
For maintenance mode, a dependency outage, or a draining instance, throw ServiceUnavailableError. It answers 503 with a stable https://daloyjs.dev/errors/service-unavailable type, an optional Retry-After, and cache-control: no-store, so a CDN never keeps serving the outage after you recover.
retryAfterSeconds must be a non-negative integer. Anything else (NaN from a missing environment variable, -1, 1.5) throws a RangeError instead of sending a malformed Retry-After header.