Errors
FougereError represents a domain error with a typed code. Handlers throw it, and the
following layers retain its code, message, and details.
throw new FougereError({
code: ErrorCode.CONFLICT,
message: `Slug '${slug}' is already taken`,
entity: 'post',
operation: 'create',
// details?: [{ path, message }] — per-field, used by VALIDATION_FAILED
// cause?: unknown
});
The codes
| Code | Meaning | HTTP projection |
|---|---|---|
VALIDATION_FAILED | invalid input — details contains per-field { path, message } | 400 |
UNAUTHORIZED | no identity | 401 |
FORBIDDEN | identity present, right absent | 403 |
NOT_FOUND | designated thing does not exist | 404 |
CONFLICT | state refuses the transition (already published, slug taken) | 409 |
SERVICE_UNAVAILABLE | a remote Frond is unreachable | 503 |
INTERNAL_ERROR | anything unexpected | 500 |
INTERNAL_ERROR is the one code whose message never leaves
The other six were written for the caller and travel whole. An INTERNAL_ERROR was not:
it may quote a path, a query or a row, so it is replaced by a constant before leaving the
process.
It is logged in the same place — masking and recording live in one function, so the
sentence exists exactly once, on the server. An operator asking "why this 500" finds it in
the logs with its cause, its entity and its operation; an attacker never sees it. If your
message must reach the caller, then it is not an INTERNAL_ERROR: give it its code.
Error propagation
- Handler throws
FougereError— the same object whether the Frond is local or remote. - Local call — the error propagates in memory, untouched.
- Remote call — the transport frames it as a JSON-RPC error (
code: -32000,data: { code, message, entity, operation, details? }) and the client side rebuilds aFougereError, including itsdetails. - In a page —
useQuery/useCommandexpose it aserror(a realFougereError);useFormFormapsVALIDATION_FAILED.detailsonto per-field errors automatically. - On the REST surface —
toHttpErrorprojects the code to the real HTTP status (table above), the full typed value indata.
The browser receives the same code, message, and per-field errors whether the Frond is
local or remote. An unreachable host produces a typed SERVICE_UNAVAILABLE error.
Check order
The site's blog performs checks in this order:
requireUser(user) // UNAUTHORIZED
→ requireOwn(id, user) // NOT_FOUND, then FORBIDDEN
→ state checks // CONFLICT (already published, empty draft)
→ realize // orm.update(…)
Next: Seeds — initializing data through operations.