13  Errors

When a fatal error occurs in your application, enables application to recover

A fatal error is a failure from which a program cannot safely recover, so execution must stop immediately.

Let’s take a look at this reprex:

library(ambiorix)

app <- Ambiorix$new()

home_get <- function(req, res) {
  xyz
}

app$get("/", home_get)

app$start(port = 5000L)

When you run this application and visit http://127.0.0.1:5000/, you get this on the console:

 21-11-2025 22:10:44 Listening on http://127.0.0.1:5000
 21-11-2025 22:10:47 GET on /
object 'xyz' not found

The application ran into a fatal error while executing the handler for “/” because of a missing variable.

Luckily, Ambiorix has a generic global error handler, defined roughly as follows:

error_handler <- function(req, res, error) {
  message(conditionMessage(error))
  res$status <- 500L
  res$send("500: Internal Server Error")
}

Because of that, the application does not crash, and the user gets this on the client:

500: Internal Server Error

13.1 Custom Handler

Just like route handlers, an error handler must be a function which takes three parameters, in this order:

  1. req: <Required>. The request object.
  2. res: <Required>. The response object.
  3. error: <Required>. The error object. This is the object returned by functions like stop().

The HTTP status code of the response given by the error handler must be 5XX eg. 500. Quoting MDN Docs:

The HTTP 500 Internal Server Error server error response status code indicates that the server encountered an unexpected condition that prevented it from fulfilling the request.

This error is a generic “catch-all” response to server issues, indicating that the server cannot find a more appropriate 5XX error to respond with.

The generic global error handler for Ambiorix is okay, but you might need a custom one depending on what you’re building.

For example, if developing a JSON data API, you’d want to return JSON from the handler as follows:

error_handler <- function(req, res, error) {
  message(conditionMessage(error))
  res$status <- 500L

  response <- list(
    status = "error",
    message = "It's not you, it's us :/"
  )

  res$json(response)
}

As noted in the examples here, we print the real error message to the console, but send back a generic message to the client. This is for security purposes, and it is advised you do the same. It ensures you do not leak critical information about your application to attackers.

13.2 Attach Handler

After defining a custom error handler, attach it to the app instance by setting the error property:

app$error <- error_handler

13.3 Route Specific

Remember that the last [optional] parameter for HTTP app methods is error?

You can use it to attach an error handler specific to that route. Even when you have a global error handler, the specific handler is the one that will be called for that route.

For example:

home_get <- function(req, res) {
  res$send("Hello, World!")
}

home_get_error_handler <- function(req, res, error) {
  message(conditionMessage(error))
  res$status <- 503L
  res$send("Oops! We're currently offline. Maintenance underway.")
}

Then you attach the handler as follows:

app$get("/", home_get, home_get_error_handler)