13 Errors
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) {
xyzabc # intentional fatal error
res$send("Hello, World!")
}
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 foundThe 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
The generic global error handler for Ambiorix is okay, but you might need a custom one depending on what you’re building.
Just like route handlers, the custom error handler you define must be a function which takes three parameters, in this order:
req: <Required>. The request object.res: <Required>. The response object.error: <Required>. The error object. This is the object returned by functions likestop().
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)
}The HTTP status code of the response given by the error handler should 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
5XXerror to respond with.
After defining a custom error handler, attach it to the app instance by setting the error property:
app$error <- error_handlerHere’s a reprex showing definition and attachment of a custom error handler:
library(ambiorix)
app <- Ambiorix$new(port = 3000L)
error_handler <- function(req, res, error) {
message(conditionMessage(error))
res$status <- 503L
res$send("Oops! We're currently offline. Maintenance underway.")
}
app$error <- error_handler
home_get <- function(req, res) {
stop("Object 'xyz' not found") # intentional fatal error
res$send("Hello, World!")
}
app$get("/", home_get)
app$start()When you visit /, you will see the custom message and not the default one.
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 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)Here is a reprex showing the distinction between the global error handler and the route specific one:
library(ambiorix)
app <- Ambiorix$new(port = 3000L)
global_error_hander <- function(req, res, error) {
message(conditionMessage(error))
res$status <- 500L
res$send("Oh no! An error occurred while processing your request :(")
}
app$error <- global_error_hander
home_get <- function(req, res) {
stop("Object 'xyz' not found") # intentional fatal error
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.")
}
about_get <- function(req, res) {
stop("Object 'xyz' not found") # another intentional fatal error
res$send("About us.")
}
app$get("/", home_get, home_get_error_handler)
app$get("/about", about_get)
app$start()Visiting / shows you the route specific error message. On the other hand, /about shows the global error message that we set.