Error Handling and Defensive Code
Prerequisites: Functions
Good functions check their inputs and fail with clear messages; good scripts handle the errors they can recover from.
Signalling problems: stop, warning, message
safe_sqrt <- function(x) {
if (!is.numeric(x)) {
stop("`x` must be numeric, not ", class(x)[1])
}
if (any(x < 0)) {
warning("Negative values replaced with NA")
x[x < 0] <- NA
}
sqrt(x)
}
safe_sqrt(c(4, -1, 9))
[1] 2 NA 3
Warning message:
In safe_sqrt(c(4, -1, 9)) : Negative values replaced with NA
stop()raises an error and stops.warning()flags a problem but continues.message()prints information (and can be silenced withsuppressMessages()).
stopifnot() checks several conditions briefly:
stopifnot(is.numeric(x), length(x) > 0).
Catching errors: tryCatch
tryCatch() runs code and decides what happens if an error or warning
occurs:
parse_number <- function(text) {
tryCatch(
{
value <- as.numeric(text)
value
},
warning = function(w) {
message("Could not parse '", text, "'")
NA
}
)
}
parse_number("42")
parse_number("forty")
[1] 42
Could not parse 'forty'
[1] NA
as.numeric("forty") normally produces a warning; here the warning
handler takes over, prints a message, and returns NA. Use
error = function(e) ... to handle errors the same way, and
conditionMessage(e) to get the message text. A finally = ...
argument runs clean-up code no matter what.
try: continue past an error
result <- try(log(-1:1 * "a"), silent = TRUE)
class(result)
cat("The script continued\n")
[1] "try-error"
The script continued
Common mistakes
- Wrapping large blocks in
tryCatchthat silently returnNA— you lose information about what went wrong. Handle only the errors you expect, and log or report the rest. - Vague error messages.
stop("invalid input")helps nobody;stop("agemust be between 0 and 120, got ", age)saves time.