Error Handling in R

How to handling error of simulation study and functions in R

R
Simulation
Error handling is important for simulation study
Author

Jihong Zhang

Published

August 31, 2024

This blog is based on Anderson and Brooke (2020) and Wickham (2024).

Anderson, Sean Kross, Roger D. Peng, and Brooke. 2020. “2.5 Error Handling and Generation | Mastering Software Development in r.” In Mastering Software Development in r. https://github.com/rdpeng/RProgDA.

1 What is Error in R?

In R, errors are signaled, or thrown, by stop() or stopifnot():

stop("Something erroneous has occurred!")
Error in eval(expr, envir, enclos): Something erroneous has occurred!
stopifnot(5 < 0)
Error: 5 < 0 is not TRUE

stopifnot() is basically a wrap-up function of if-condition and stop to detect if any of the expressions are not all TRUE.

Sometimes, error messages are meaningful and deliberately created/controlled by function authors while it is meaningless and subjected to be controlled by R users. Here are some scenairos:

Example 1 - Uncontrolled error: You want to ran a for iteration while there is some error message in some iterations. The error message contains nothing but the stopping signals from data analysis for certain iterations. This type of error message make not much sense to understanding why this error occur.

Example 2 - Controlled error: You are using a function inappropriately and this function throws you some message. If the function is well coded, the error message does tell you the “Problem statement” using must or can't:

dplyr::nth(1:10, 1:2)
Error in `dplyr::nth()`:
! `n` must have size 1, not size 2.
as_vector(environment())
Error in as_vector(environment()): could not find function "as_vector"

Some functions also provide hints when error occurs:

dplyr::filter(iris, Species = "setosa")
Error in `dplyr::filter()`:
! We detected a named input.
ℹ This usually means that you've used `=` instead of `==`.
ℹ Did you mean `Species == "setosa"`?

2 Functions for error handling in R

Condition handling tools, like withCallingHandlers(), tryCatch(), and try() allow you to take specific actions when a condition occurs. For example, if you’re fitting many models, you might want to continue fitting the others even if one fails to converge.

2.1 Use try to ignore errors

With the try function, you can “hide” or ignore error signals to continue the execution:

## Function without try
fun0 <- function() {
  x <- b
  x <- 3
  print(x)
}
fun0()
Error in fun0(): object 'b' not found
## Function with try
fun1 <- function() {
  try(x <- b)
  x <- 3
  print(x)
}
fun1()
Error in try(x <- b) : object 'b' not found
[1] 3
## Multiple errors in one function
options(error = function() traceback(3))
fun1 <- function() {
  try(x <- b1) # This will throw error but will keep the execution
  try(x <- 3) # This will not throw error and will execute the code normally
  print(x)
  x <- b2 # This will also throw error but will stop the execution
  print(x)
}
fun1()
Error in try(x <- b1) : object 'b1' not found
[1] 3
Error in fun1(): object 'b2' not found

The function with try does not stop executing the rest of the syntax after error occurs (x <- b). However, there is one issue of try(): it only ignores single error message at once and you have to know where the error comes from beforehand.

In practice, you can ignore error when running the simulation:

for (x in list(1, 2, "not a number")) {
  y <- 3*x
  print(paste0("3x= ", y))
}
[1] "3x= 3"
[1] "3x= 6"
Error in 3 * x: non-numeric argument to binary operator
for (x in list(1, 2, "not a number")) {
  # when x iterate over thirt element, try() ignore the error
  # and still keep y = 2
  try(y  <- 3*x, silent = TRUE) 
  try(print(paste0("3x= ", y)))
}
[1] "3x= 3"
[1] "3x= 6"
[1] "3x= 6"

If you want to assign b to x if b is available and assign NA to x if b is unavailable, you can:

  1. Use if condition to detect whether b is available or not;
  2. Use tryCatch() to use condition name (error, warning) as parameter

2.2 Use tryCatch to handle errors

tryCatch() is a general tool for handling conditions: in addition to errors, you can take different actions for warnings, messages, and interrupts (Wickham 2024).

Wickham, Hadley. 2024. “8 Conditions | Advanced r.” In Advanced r. https://adv-r.hadley.nz/conditions.html#errors-1.
# when b is unavailable
tryCatch({
  x <- b
}, error = function(e) {x <<- NA})
x
[1] NA
# when b is available
b <- 3
tryCatch({
  x <- b
}, error = function(e) {x <<- NA})
x
[1] 3

The tryCatch() will catch the error condition (warning) and execute another function you defined (error = function(e) in the example). Note that if you want to change x’s value in error function when x<-b has error, you need to use <<- assign symbol to transfer x value into global environment.

2.3 Use withCallingHandlers to handle errors

withCallingHandlers({
  x <- b2
}, error = function(e) {x <<- NA})
Error in withCallingHandlers({: object 'b2' not found
print(x)
[1] NA
Back to top