#Error Handling
Alepha maps errors to HTTP responses through the HttpError class. Unhandled errors become 500 Internal Server Error.
#HttpError
Throw an HttpError to return a specific HTTP status code and message:
1import { HttpError } from "alepha/server";2 3throw new HttpError({ status: 404, message: "Product not found" });
#Named Error Classes
Alepha provides error subclasses for common HTTP statuses:
1import { 2 BadRequestError, 3 UnauthorizedError, 4 ForbiddenError, 5 NotFoundError, 6 ConflictError, 7 ValidationError, 8} from "alepha/server"; 9 10throw new NotFoundError("User not found");11throw new BadRequestError("Invalid input");12throw new UnauthorizedError("Not authenticated");13throw new ForbiddenError("Access denied");14throw new ConflictError("Entity already exists");15throw new ValidationError("Validation has failed");
Each class has a sensible default message:
| Class | Status | Default Message |
|---|---|---|
BadRequestError |
400 | "Invalid request body" |
ValidationError |
400 | "Validation has failed" |
UnauthorizedError |
401 | "Not allowed to access this resource" |
ForbiddenError |
403 | "No permission to access this resource" |
NotFoundError |
404 | "Resource not found" |
ConflictError |
409 | "Entity already exists" |
#Error Identification
Check whether an error is an HTTP error, optionally filtering by status:
1import { HttpError } from "alepha/server";2 3if (HttpError.is(error)) {4 // any HTTP error5}6 7if (HttpError.is(error, 404)) {8 // specifically a 4049}
#Error Chaining
Pass a cause to preserve the original error context:
1try {2 await externalService.call();3} catch (error) {4 throw new HttpError(5 { status: 502, message: "Upstream service failed" },6 error,7 );8}
The cause is included in the error JSON response under the cause field:
1{2 "error": "BadGatewayError",3 "status": 502,4 "message": "Upstream service failed",5 "cause": {6 "name": "FetchError",7 "message": "Connection refused"8 }9}
#Error Response Format
All HTTP errors are serialized as JSON with a consistent structure:
1{2 "error": "NotFoundError",3 "status": 404,4 "message": "User not found",5 "requestId": "abc-123"6}
The error field is the class name (e.g. NotFoundError, ConflictError). For plain HttpError instances, it is derived from the status code.
#Status Code Mapping
HttpError maps status codes to error names automatically:
| Status | Error Name |
|---|---|
| 400 | BadRequestError |
| 401 | UnauthorizedError |
| 403 | ForbiddenError |
| 404 | NotFoundError |
| 409 | ConflictError |
| 413 | PayloadTooLargeError |
| 429 | TooManyRequestsError |
| 500 | InternalServerError |
| 502 | BadGatewayError |
| 503 | ServiceUnavailableError |
#Global Error Handling
Use the server:onError hook to intercept errors across all routes:
1import { $hook } from "alepha"; 2import { $logger } from "alepha/logger"; 3 4class ErrorHandler { 5 log = $logger(); 6 7 onError = $hook({ 8 on: "server:onError", 9 handler: async ({ error, request }) => {10 this.log.error("Request failed", {11 error,12 path: request.url.pathname,13 method: request.method,14 });15 },16 });17}
The server:onError hook fires after the error is caught but before the response is sent. The response body is reset at this point and can be modified via request.reply.
#Schema Validation Errors
When a request fails schema validation (invalid body, params, or query), Alepha throws a ValidationError (status 400) automatically. You do not need to validate request data manually.