Robust scripts and packages must fail clearly and clean up after themselves.
R's condition system has three main signals: stop() raises an error that aborts the call, warning() signals a warning that by default is printed after the call finishes, and message() emits an informational message (suppress with suppressMessages).
tryCatch(expr, error = function(e) ..., warning = function(w) ..., finally = ...) evaluates expr and, if a matching condition is signalled, exits to the handler, whose value becomes the result. conditionMessage(e) extracts the text. try(expr, silent = TRUE) is a lighter variant returning an object of class "try-error".
withCallingHandlers is different: its handlers run without unwinding, so after a warning handler returns, the code continues. Combine it with invokeRestart("muffleWarning") to log and silence warnings.
You can signal custom condition classes, which callers can catch precisely: stop(errorCondition("not found", class = "notFoundError", data = list(id = 7))). The rlang package's abort() makes this convenient.
Use on.exit(close(con), add = TRUE) inside functions to guarantee cleanup whether the function returns or errors. stopifnot() is a concise way to assert preconditions, and options(warn = 2) turns warnings into errors while debugging; traceback() and browser() help find where it happened.
Example
safe_log <- function(x) {
tryCatch({
if (!is.numeric(x)) stop(errorCondition("need a number", class = "typeError"))
log(x)
},
typeError = function(e) { message("type problem: ", conditionMessage(e)); NA },
warning = function(w) { message("warning caught: ", conditionMessage(w)); NaN })
}
print(safe_log(100)) #> [1] 4.60517
print(safe_log("a")) #> type problem: need a number / [1] NA
print(safe_log(-1)) #> warning caught: NaNs produced / [1] NaN
res <- withCallingHandlers(
{ warning("first"); "kept going" },
warning = function(w) { message("logged: ", conditionMessage(w))
invokeRestart("muffleWarning") })
print(res) #> logged: first / [1] "kept going"
f <- function() { on.exit(cat("cleanup\n")); stop("boom") }
try(f(), silent = TRUE) #> cleanupRemember
stoperrors,warningwarns,messageinformstryCatchexits to the handler;withCallingHandlerscontinues- Custom condition classes let callers catch precisely
on.exit(..., add = TRUE)guarantees cleanup
Watch out for
- Catching every error with a broad handler and returning NA silently, hiding real bugs.