12.5 Error Handling and Debugging
When writing R code, you will inevitably encounter errors, warnings, and unexpected behavior. In this section, you will learn how to handle these situations gracefully and debug your code effectively.
12.5.1 Errors, Warnings, and Messages
R has three types of signals that communicate different levels of severity:
- Errors (produced by
stop()): The code cannot continue. Execution halts immediately. - Warnings (produced by
warning()): Something unexpected happened, but the code can still continue. - Messages (produced by
message()): Informational output that does not indicate a problem.
12.5.2 Generating Custom Errors and Warnings
When writing your own functions, you should signal problems clearly to the user.
safe_divide <- function(x, y) {
if (y == 0) {
stop("Division by zero is not allowed.")
}
if (x == 0) {
warning("Numerator is zero; result will be zero.")
}
x / y
}
safe_divide(10, 2)
#> [1] 5
safe_divide(0, 5)
#> [1] 0
safe_divide(10, 0)
#> Error in `safe_divide()`:
#> ! Division by zero is not allowed.12.5.3 Handling Errors with try() and tryCatch()
12.5.3.1 try(): Keep Going After an Error
The try() function wraps an expression so that if it produces an error, execution continues instead of stopping.
When silent = TRUE, the error message is suppressed. You can check whether an error occurred using inherits(result, "try-error").
12.5.3.2 tryCatch(): Fine-Grained Error Handling
The tryCatch() function provides more control by letting you specify handlers for different types of conditions.
safe_log <- function(x) {
tryCatch(
log(x),
warning = function(w) {
message("Warning caught: ", conditionMessage(w))
NA
},
error = function(e) {
message("Error caught: ", conditionMessage(e))
NA
}
)
}
safe_log(10)
#> [1] 2.302585
safe_log(-1)
#> Warning caught: NaNs produced
#> [1] NA
safe_log("abc")
#> Error caught: non-numeric argument to mathematical function
#> [1] NAA practical use case is applying a function to a list of inputs where some might cause errors:
12.5.4 Debugging Tools
When your code produces unexpected results but no error, you need to investigate. R provides several debugging tools.
12.5.4.1 traceback(): Find Where an Error Occurred
After an error, calling traceback() shows the sequence of function calls that led to the error.
12.5.4.2 browser(): Pause and Inspect
Inserting browser() inside a function pauses execution at that point and opens an interactive debugging session where you can inspect variables.
my_function <- function(x) {
y <- x^2
browser() # execution pauses here
z <- y + 1
z
}
my_function(5)In the debugging session, you can type variable names to see their values, n to step to the next line, c to continue, or Q to quit.
12.5.5 Exercises
Write a function
safe_sqrt(x)that usestryCatch()to returnNAwhen the input is negative (instead of producing a warning) and when the input is not numeric (instead of producing an error). Test it withsafe_sqrt(9),safe_sqrt(-4), andsafe_sqrt("hello").Given a list of file paths
files <- c("data/file1.csv", "data/file2.csv", "data/nonexistent.csv"), write code usingtry()inside aforloop that attempts to read each file withread.csv()and prints a message for files that cannot be read. (You do not need actual files; just observe the error handling.)Write a function
divide_all(x, y)that divides each element of vectorxby the corresponding element of vectory. UsetryCatch()to handle cases whereycontains zeros, returningInffor those positions instead of an error.Use
browser()inside a simple function to practice stepping through code. Write a function that computes the cumulative sum of a vector using aforloop, and usebrowser()to inspect the intermediate values.