Enumerated Custom Errors in R

September 4, 2020 • 11:58 AM

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:

  1. You would want to replace some.event with your own event name. This also works with outputs like tableOutput() in pace of observeEvent().
  2. Construct the error message here.
  3. Throw the error for handling.
  4. To be extra-safe, we assign “Unknown Error” to the title if none exists. Recall that R’s base simpleError classes have no title property. The expression in the tryCatch() 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 the title 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.