name: noony-error-handling description: Use when throwing errors, handling error types, mapping errors to HTTP status codes, wrapping external API errors, or implementing custom error classes in Noony handlers. Covers the full error class hierarchy, cause chaining, ErrorHandlerMiddleware lifecycle, and ServiceError for non-HTTP contexts.
skill:noony-error-handling
Does exactly this
Covers built-in error classes with HTTP status codes, cause chaining for wrapping errors, custom error types, and ErrorHandlerMiddleware lifecycle. ErrorHandlerMiddleware must be at position 1 in the canonical middleware order (see noony-middleware-ordering).
When to use
- "Throw an error"
- "Handle different error types"
- "Map errors to HTTP status codes"
- "Wrap external API errors"
- "Custom error class"
Do not use this skill when
- You need middleware ordering guidance ->
noony-middleware-orderingis the canonical reference - You need body validation schemas ->
noony-validation-schemasfor Zod integration - You need validation error configuration ->
noony-validation-schemashandlesValidationErrorresponses - You need custom middleware development ->
noony-middleware-development
Steps
- Import typed error from
@noony-serverless/core— never throw genericError()-> Seereferences/error-hierarchy.md#error-class-hierarchy-tablefor the complete error list with status codes - Ensure ErrorHandlerMiddleware is at position 1 per
noony-middleware-ordering— itsonErrorfires last in reverse order, giving it final authority over error responses - Throw typed error in handler —
ErrorHandlerMiddlewarecatches and formats the JSON response automatically - For external API errors, wrap with cause chaining to preserve the original stack trace:
throw new InternalServerError('API failed', originalError); - Never call
context.res.status().json()for errors — always throw typed errors instead - For domain-specific errors, extend
HttpErrorwith a custom status code -> Seereferences/error-hierarchy.md#custom-error-classesfor the pattern - Use
ServiceErrorin service layers that shouldn't know about HTTP -> Seereferences/error-hierarchy.md#serviceerror-for-non-http-contexts
Rules
- Always throw typed errors (
NotFoundError,ForbiddenError) — genericError()becomes an opaque 500 ErrorHandlerMiddlewaremust be first (position 1) in the middleware chain pernoony-middleware-ordering— itsonErrorfires last in reverse, giving final authority over error responses- Use cause chaining for wrapping:
new InternalServerError(message, causeError)— preserves the original stack for logging while keeping the client response clean ServiceErrorfor non-HTTP errors (business logic, internal services) — translate toHttpErrorsubclass in the handler- HTTP status codes are automatic based on error type — no manual status setting needed
Anti-patterns
context.res.status(404).json()— bypasses error formatting and loggingthrow new Error('Not found')— loses HTTP status code, becomes 500catch (err) { /* ignore */ }— silent failures hide bugs- Wrapping errors without cause chaining — original error context lost for debugging
ErrorHandlerMiddlewarenot at position 1 pernoony-middleware-ordering— errors from earlier middlewares go uncaught
Done when
- You know which error to throw for 400, 401, 403, 404, 409, 500
- You understand cause chaining pattern for wrapping external errors
- You can write custom error classes extending
HttpError - ErrorHandlerMiddleware is at position 1 per
noony-middleware-ordering - You know
ErrorHandlerMiddlewareruns on the error path (reverse order, fires last)
If you need more detail
-> references/error-hierarchy.md — Complete error table with status codes, cause chaining examples, custom errors, ErrorHandlerMiddleware lifecycle, ServiceError patterns, debugging with DEBUG_API_RESPONSE, testing error paths