R Error Handling
Errors, warnings, and messages are R's three ways of signaling problems. Without error handling, a single failure crashes your entire script. With proper error handling, you catch failures gracefully, log what went wrong, and let the rest of your code continue running.
R's Three Signal Types
Type Severity Behavior Function
──────────────────────────────────────────────────────────────────
Error Fatal Stops execution stop("message")
Warning Non-fatal Continues, alerts warning("message")
Message Informational Just prints message("message")
tryCatch() — Catch and Handle Errors
result <- tryCatch({
log(-1) # produces warning + NaN
"success"
}, warning = function(w) {
cat("Warning caught:", conditionMessage(w), "\n")
"handled warning"
}, error = function(e) {
cat("Error caught:", conditionMessage(e), "\n")
NA
}, finally = {
cat("This always runs\n")
})
print(result)
tryCatch flow:
Run expression
│
├── Error? ──► error handler ──► return its value
├── Warning? ─► warning handler → return its value
├── Message? ─► message handler → return its value
└── No signal → return expression value
finally block always runs (cleanup code)
tryCatch() in a Loop
# Process a list — skip failures, continue with rest
files <- c("data1.csv", "missing.csv", "data2.csv")
results <- list()
for (f in files) {
results[[f]] <- tryCatch({
read.csv(f)
}, error = function(e) {
cat("Skipped", f, ":", conditionMessage(e), "\n")
NULL
})
}
# Count successful loads
success <- sum(!sapply(results, is.null))
cat("Loaded:", success, "of", length(files), "files\n")
withCallingHandlers() — Handle Without Stopping
# Unlike tryCatch, does not stop execution after handling
withCallingHandlers({
log(-1) # warning
log(10) # fine
sqrt(-4) # NaN warning
}, warning = function(w) {
cat("Warning:", conditionMessage(w), "\n")
invokeRestart("muffleWarning") # suppress the warning printout
})
Generating Your Own Conditions
safe_sqrt <- function(x) {
if (!is.numeric(x)) stop("Input must be numeric")
if (x < 0) warning("Negative input: returning NaN")
if (x == 0) message("Input is zero — result will be zero")
sqrt(abs(x))
}
tryCatch(safe_sqrt("hello"), error=function(e) cat("ERROR:", conditionMessage(e)))
tryCatch(safe_sqrt(-9), warning=function(w) cat("WARN:", conditionMessage(w)))
try() — Simpler Alternative
# try() is simpler than tryCatch for basic error catching
result <- try(read.csv("nonexistent.csv"), silent=TRUE)
if (inherits(result, "try-error")) {
cat("File not found — using default data\n")
result <- data.frame(x=1:5, y=1:5)
}
safely() from purrr — Functional Error Handling
library(purrr)
safe_log <- safely(log)
safe_log(10) # $result: 2.30 $error: NULL
safe_log(-1) # $result: NULL $error: <simpleWarning...>
safe_log("abc") # $result: NULL $error: <simpleError...>
# Apply safely to a list
results <- map(list(10, -1, "abc", 100), safely(log))
ok_vals <- keep(results, ~is.null(.x$error))
cat("Successful:", length(ok_vals), "\n")
Logging Errors to a File
log_error <- function(msg, file="error_log.txt") {
timestamp <- format(Sys.time(), "%Y-%m-%d %H:%M:%S")
write(paste(timestamp, "-", msg), file=file, append=TRUE)
}
tryCatch({
read.csv("bad_file.csv")
}, error = function(e) {
log_error(conditionMessage(e))
cat("Error logged. Analysis skipped.\n")
})
tryCatch vs withCallingHandlers
tryCatch: withCallingHandlers: ──────────────────────────── ────────────────────────────────────── Exits expression on catch Resumes execution after handling Returns handler value Continues from point of signal Good for "fail and recover" Good for "log but continue"
Error handling transforms fragile scripts into robust pipelines. Any script that reads files, calls APIs, or processes user-supplied data should wrap risky operations in tryCatch. The few extra lines of error handling code prevent hours of debugging when something inevitably goes wrong in production.
