How do I save warnings and errors as output from a function?
error-handling, r, try-catch
Solution
Maybe this is the same as your solution, but I wrote a `factory` to convert plain old functions into functions that capture their values, errors, and warnings, so I can
test <- function(i)
switch(i, "1"=stop("oops"), "2"={ warning("hmm"); i }, i)
res <- lapply(1:3, factory(test))
with each element of the result containing the value, error, and / or warnings. This would work with user functions, system functions, or anonymous functions (`factory(function(i) ...)`). Here's the factory
factory <- function(fun)
function(...) {
warn <- err <- NULL
res <- withCallingHandlers(
tryCatch(fun(...), error=function(e) {
err <<- conditionMessage(e)
NULL
}), warning=function(w) {
warn <<- append(warn, conditionMessage(w))
invokeRestart("muffleWarning")
})
list(res, warn=warn, err=err)
}
and some helpers for dealing with the result list
.has <- function(x, what)
!sapply(lapply(x, "[[", what), is.null)
hasWarning <- function(x) .has(x, "warn")
hasError <- function(x) .has(x, "err")
isClean <- function(x) !(hasError(x) | hasWarning(x))
value <- function(x) sapply(x, "[[", 1)
cleanv <- function(x) sapply(x[isClean(x)], "[[", 1)
Problem
I'm using `lapply` to run a complex function on a large number of items, and I'd like to save the output from each item (if any) together with any warnings/errors that were produced so that I can tell which item produced which warning/error. I found a way to catch warnings using `withCallingHandlers` (described here). However, I need to catch errors as well. I can do it by wrapping it in a `tryCatch` (as in the code below), but is there a better way to do it? ``` catchToList <- function(expr) { val <- NULL myWarnings <- NULL wHandler <- function(w) { myWarnings <<- c(myWarnings, w$message) invokeRestart("muffleWarning") } myError <- NULL eHandler <- function(e) { myError <<- e$message NULL } val <- tryCatch(withCallingHandlers(expr, warning = wHandler), error = eHandler) list(value = val, warnings = myWarnings, error=myError) } ``` Sample output of this function is: ``` > catchToList({warning("warning 1");warning("warning 2");1}) $value [1] 1 $warnings [1] "warning 1" "warning 2" $error NULL > catchToList({warning("my warning");stop("my error")}) $value NULL $warnings [1] "my warning" $error [1] "my error" ``` There are several questions here on SO that discuss `tryCatch` and error handling, but none that I found that address this particular issue. See How can I check whether a function call results in a warning?, warnings() does not work within a function? How can one work around this?, and How to tell lapply to ignore an error and process the next thing in the list? for the most relevant ones.