setErrorHandler + httpErrors From @fastify/sensible
Error Responses
Customize the JSON error payload with setErrorHandler and raise structured errors using the httpErrors helper from @fastify/sensible.
What you'll learn
- Throw httpErrors.notFound and badRequest with messages
- Set a global error handler for a consistent response shape
- Map validation errors to a user-friendly format
Every API ends up with a custom error shape — RFC 7807 problem details, a JSON:API errors array,
or a simple { code, message }. Fastify supplies the pieces; you wire them together.
Raising Errors
npm i @fastify/sensible await app.register(import('@fastify/sensible'));
app.get('/posts/:id', async (req) => {
const post = await db.posts.find(req.params.id);
if (!post) throw app.httpErrors.notFound('No post with that id');
return post;
}); The thrown error carries statusCode, error, and message — Fastify’s default handler turns
that into a JSON body.
A Global Error Handler
app.setErrorHandler((err, req, reply) => {
req.log.error({ err, reqId: req.id }, 'request failed');
if (err.validation) {
return reply.code(400).send({
code: 'validation_failed',
message: 'One or more fields are invalid',
details: err.validation,
});
}
const status = err.statusCode ?? 500;
reply.code(status).send({
code: status === 500 ? 'internal_error' : err.code ?? 'error',
message: status === 500 ? 'Something went wrong' : err.message,
});
}); Hide the message on 500s in production — surface stack traces only to your log aggregator.
404 Handler
setNotFoundHandler is the matching helper for unmatched routes:
app.setNotFoundHandler((req, reply) => {
reply.code(404).send({ code: 'not_found', message: 'Route not found' });
});