When designing a complex Shiny app in R, you might want to present different kinds of errors that you expect your users to encounter. These errors should have a summary to show the user what went wrong. There should also be a longer description to give the user more information about the error, as well as possible steps to troubleshoot.
You would also want your custom errors enumerated, so that the messages are in one place for easy management and update. This is critical as your app grows in complexity, and it’s really just good practice. In this post, we will explore just that: design your own custom error cases to display helpful error messages in Shiny apps.
Creating Custom Errors
The base R package comes with simpleError
class, which inherits from error
and condition
classes. Normally, when you want to throw an error, you can use stop(someError)
.
The limitation of simpleError
is that it only bears a message
property. It is very limited when you want to show complex information to users.
But we can build an extension on that with the following code:
APP_ERROR <- function(title, description, call = NULL, ...) {
structure(
list(
title = title,
message = description,
call = call,
...
),
class = c("error", "condition")
)
}
In the code, APP_ERROR
becomes a constructor of your custom error class. It inherits from error
and condition
classes, notice we extended the error class to include a title
property.
You can now throw your custom error like this:
someError <- APP_ERROR("Some Error", "This is a testing error.")
stop(someError)
In Shiny app, you can pass this error message to the user with showModal()
, like this:
ui <- fluidPage (
actionButton("some.event", label = "Error!")
)
server <- function(input, output, session) {
observeEvent(input$some.event, { # 1. replace with your event
TryCatch({
someError <- APP_ERROR(
title = "Some Error",
description = "This is a testing error."
) # 2. construct your custom error message
stop(someError) # 3. throw the error for handling
}, error = function(err) {
title = "Unknown Error" # 4.
if (!is.null(err$name)) {
title = err$name
}
dialog = modalDialog(err$message, title = title)
showModal(dialog)
})
})
}
Some notes:
- You would want to replace
some.event
with your own event name. This also works with outputs liketableOutput()
in pace ofobserveEvent()
. - Construct the error message here.
- Throw the error for handling.
- To be extra-safe, we assign “Unknown Error” to the
title
if none exists. Recall that R’s basesimpleError
classes have notitle
property. The expression in thetryCatch()
body is quite straightforward, and it can’t throw any other error; but in a real application, anything can happen — you don’t want your error handling code to throw another error when thetitle
property is null.
Make Errors Enumerated
Since we’ve covered custom errors, putting them down for enumeration is simple: you can achieve this with R’s list
. For a simple database log-in app, we can have the following error cases:
LOGIN_ERRORS <- list (
usernameEmpty = APP_ERROR("Username Empty", "Please enter your username."),
passwordEmpty = APP_ERROR("Password Empty", "Please enter your password."),
wrongCredentials = APP_ERROR("Wrong Credentials", "The username and password you provided were denied by the server. Please check and try again.")
)
Then you can throw these errors with very succinct expressions:
TryCatch({
if (input$username != "") {
stop(LOGIN_ERRORS$usernameEmpty)
}
if (input$password != "") {
stop(LOGIN_ERRORS$passwordEmpty)
}
loggedIn <- logIn(username, password) # Your own log-in logic
if (!loggedIn) {
stop(MY_CUSTOM_ERRORS$wrongCredentials)
}
}, error = function(err) {
# ... handling expression, such as displaying a dialog
})
And there you have it!
Summary
Enumerated custom errors are very helpful in a complex R Shiny app. The custom classes help you structure your app and deliver helpful information to users, and enumeration will keep your error codes manageable as the app grows.