Skip to content

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:

js
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:

js
module.exports = {
  fn: async function () {
    try {
      await dangerousOperation()
    } catch (error) {
      return sails.inertia.handleServerError(this.req, this.res, error)
    }
  }
}

Behavior by environment

EnvironmentInertia RequestNon-Inertia Request
DevelopmentError modal with stack traceHTML error page with stack trace
ProductionRedirect back with flash errorGeneric 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:

js
// 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:

js
// 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

  1. Use specific exits: Define clear exit types for different error scenarios
js
exits: {
  success: { responseType: 'inertia' },
  notFound: { responseType: 'notFound' },
  badRequest: { responseType: 'badRequest' },
  serverError: { responseType: 'serverError' }
}
  1. Log errors: Always log server errors for monitoring
js
catch (error) {
  sails.log.error('Payment processing failed:', error)
  throw { serverError: error }
}
  1. User-friendly production errors: Use flash messages for production
js
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

TypeResponseUse case
badRequest400 + redirect with errorsForm validation failures
serverError500 + error modal/redirectUnexpected exceptions

Use badRequest for validation errors (user can fix) and serverError for unexpected failures (bugs, external service failures).

All open source projects are released under the MIT License.