Error handling is centralized in src/error/ and mapped to responses in Server.handleError.
HttpError(http-error.ts:67-77):new HttpError(status, message?, description?, cause?).messagedefaults toSTATUS_MESSAGES[status];this.name = this.constructor.nameso subclass names survive serialization.isHttpError(value)type guard (http-error.ts:80-82).STATUS_MESSAGES(http-error.ts:4-60): every status phrase, the source of default messages.
createHttpErrorClass(status, className, defaultMessage) (http-error.ts:94-112) generates ~60 named classes in errors.ts (BadRequest, NotFound, ImATeapot, NetworkConnectTimeoutError, …), each with a static status.
Three classes take constructor args because their message needs data:
MethodNotAllowed(method)(errors.ts:21-25)PayloadTooLarge(maxBodySize)(errors.ts:55-59)UnsupportedMediaType(contentType?)(errors.ts:67-76)
throw new NotFound(); // → 404 { status: 404, message: "Not Found" }
throw new MethodNotAllowed("PATCH"); // → 405 with the method in the message
throw new Error("boom"); // → 500 { status: 500, message: "Internal Server Error" }Server.handleError (server.ts:203-235):
- Runs the
onErrorhook first (in a try/catch — a throwing hook can't kill the process,server.ts:206-209). - Bails if the response is already committed.
HttpError→ itsstatus/message/descriptionas JSON.- Anything else →
500with the generic message (the real error goes toonError, not to the client).
WebSocketProtocolError(ws/protocol-error.ts:7-15) carries acloseCode(defaultProtocolError) used byWebSocketConnection.fail()to send a Close frame before destroying.- In the upgrade path,
WebSocketUpgrader.handleWebSocketErrorcalls theonErrorhook then destroys the socket (http/server/websocket.ts:142) — a failed upgrade gets a raw rejection, not a 500 JSON.
ServerOptions.onError?: (error, ctx) => … (server/types.ts). Runs for both HTTP and WebSocket errors, always before the response is produced.