Error handling
The Boring JavaScript Stack provides built-in error handling that integrates seamlessly with Inertia.js, including a development error modal that shows stack traces without losing your page state.
The error modal
When a server error occurs during an Inertia request in development, instead of showing a blank error page, The Boring Stack displays a styled error modal with the full stack trace. This lets you debug issues without losing your current page state or form data.
The error modal is automatically enabled in development (NODE_ENV !== 'production') and shows:
- HTTP status code
- Error name and message
- Request method and URL
- Full stack trace with syntax highlighting
In production, errors redirect back to the previous page with a flash message.
Using the serverError response
Define the serverError exit in your action:
module.exports = {
exits: {
success: {
responseType: 'inertia'
},
serverError: {
responseType: 'serverError'
}
},
fn: async function () {
try {
const data = await someRiskyOperation()
return { page: 'dashboard', props: { data } }
} catch (error) {
throw { serverError: error }
}
}
}Direct usage
You can also call handleServerError directly:
module.exports = {
fn: async function () {
try {
await dangerousOperation()
} catch (error) {
return sails.inertia.handleServerError(this.req, this.res, error)
}
}
}Behavior by environment
| Environment | Inertia Request | Non-Inertia Request |
|---|---|---|
| Development | Error modal with stack trace | HTML error page with stack trace |
| Production | Redirect back with flash error | Generic error page |
Customizing the error modal
The error modal HTML is generated by the handleServerError function. If you need to customize it, you can create your own response handler:
// api/responses/serverError.js
module.exports = function serverError(error) {
const sails = this.req._sails
const isDev = process.env.NODE_ENV !== 'production'
const isInertia = this.req.header('X-Inertia')
if (isInertia && isDev) {
// Your custom error HTML
return this.res.status(500).send(buildCustomErrorHtml(error))
}
// Fall back to default handling
return sails.inertia.handleServerError(this.req, this.res, error)
}Global error handling
For catching unhandled errors globally, you can use Sails' config/http.js middleware:
// config/http.js
module.exports.http = {
middleware: {
order: [
// ... other middleware
'errorHandler'
],
errorHandler: function (err, req, res, next) {
if (req.header('X-Inertia')) {
return sails.inertia.handleServerError(req, res, err)
}
return next(err)
}
}
}Error handling best practices
- Use specific exits: Define clear exit types for different error scenarios
exits: {
success: { responseType: 'inertia' },
notFound: { responseType: 'notFound' },
badRequest: { responseType: 'badRequest' },
serverError: { responseType: 'serverError' }
}- Log errors: Always log server errors for monitoring
catch (error) {
sails.log.error('Payment processing failed:', error)
throw { serverError: error }
}- User-friendly production errors: Use flash messages for production
catch (error) {
sails.log.error('Operation failed:', error)
sails.inertia.flash('error', 'Something went wrong. Please try again.')
return '/dashboard'
}Validation errors vs server errors
| Type | Response | Use case |
|---|---|---|
badRequest | 400 + redirect with errors | Form validation failures |
serverError | 500 + error modal/redirect | Unexpected exceptions |
Use badRequest for validation errors (user can fix) and serverError for unexpected failures (bugs, external service failures).