get stack trace on tryCatch'ed error in R - r

This is related to some other questions, but I can't seem to figure out how to apply the answer, so I'm asking a new question.
I'm trying to figure out an uninformative error from a piece of code that looks like this:
tryCatch(MainLoop(),
error=function(e) { fatal(lgr, paste('caught fatal error:', as.character(e)));
exit.status <<- 1 })
The problem is that the error appears to be related to something buried in a library function:
Error in nrow(x): (subscript) logical subscript too long
That nrow is not in my code, as the C-level error above only applies to a type of indexing that never happens in any of my nrow calls.
So I'd really like to get a stack trace from within that tryCatch. Here's an analogous problem:
x <- function() { y(); }
y <- function() { z(); }
z <- function() { stop("asdf") }
> x()
Error in z() : asdf
> tryCatch(x(), error=function(e) { print(conditionCall(e)) } )
z()
> tryCatch(x(), error=function(e) { dump.frames() } )
> last.dump
$`tryCatch(x(), error = function(e) {
dump.frames()
})`
<environment: 0x1038e43b8>
$`tryCatchList(expr, classes, parentenv, handlers)`
<environment: 0x1038e4c60>
$`tryCatchOne(expr, names, parentenv, handlers[[1]])`
<environment: 0x1038e4918>
$`value[[3]](cond)`
<environment: 0x1038ea578>
attr(,"error.message")
[1] "asdf"
attr(,"class")
[1] "dump.frames"
How do I get the stack trace that includes the call to y()? Do I have to stop using tryCatch? What's a better way?

For interactive use one might trace(stop, quote(print(sys.calls()))) to print the call stack at the time stop() is invoked.
From ?tryCatch,
The function 'tryCatch' evaluates its expression argument in a
context where the handlers provided in the '...' argument are
available.
whereas
Calling handlers are established by 'withCallingHandlers'...
the handler is called... in the context where the condition
was signaled...
so
> withCallingHandlers(x(), error=function(e) print(sys.calls()))
[[1]]
withCallingHandlers(x(), error = function(e) print(sys.calls()))
[[2]]
x()
[[3]]
y()
[[4]]
z()
[[5]]
stop("asdf")
[[6]]
.handleSimpleError(function (e)
print(sys.calls()), "asdf", quote(z()))
[[7]]
h(simpleError(msg, call))
Error in z() : asdf
This is thwarted if there is an inner tryCatch
withCallingHandlers({
tryCatch(x(), error=function(e) stop("oops"))
}, error=function(e) print(sys.calls()))
as we only have access to the call stack after the tryCatch has 'handled' the error.

Yes, it is possible. It is not too elegant in coding, but very helpful in output!
Any comments are welcome!
I put it in my misc package, use it from there if you want the documentation.
https://github.com/brry/berryFunctions/blob/master/R/tryStack.R
The next CRAN version is planned to be released soon, until then:
devtools::install_github("brry/berryFunctions")
# or use:
source("http://raw.githubusercontent.com/brry/berryFunctions/master/R/instGit.R")
instGit("brry/berryFunctions")
library(berryFunctions)
?tryStack
Here it is for fast reference:
tryStack <- function(
expr,
silent=FALSE
)
{
tryenv <- new.env()
out <- try(withCallingHandlers(expr, error=function(e)
{
stack <- sys.calls()
stack <- stack[-(2:7)]
stack <- head(stack, -2)
stack <- sapply(stack, deparse)
if(!silent && isTRUE(getOption("show.error.messages")))
cat("This is the error stack: ", stack, sep="\n")
assign("stackmsg", value=paste(stack,collapse="\n"), envir=tryenv)
}), silent=silent)
if(inherits(out, "try-error")) out[2] <- tryenv$stackmsg
out
}
lower <- function(a) a+10
upper <- function(b) {plot(b, main=b) ; lower(b) }
d <- tryStack(upper(4))
d <- tryStack(upper("4"))
cat(d[2])
d <- tryStack(upper("4"))
This is the error stack:
tryStack(upper("4"))
upper("4")
lower(b)
Error in a + 10 : non-numeric argument to binary operator

I am a fan of evaluate::try_capture_stack().
x <- function() {
y()
}
y <- function() {
z()
}
z <- function() {
stop("asdf")
}
env <- environment()
e <- evaluate::try_capture_stack(quote(x()), env)
names(e)
#> [1] "message" "call" "calls"
e$calls
#> [[1]]
#> x()
#>
#> [[2]]
#> y()
#>
#> [[3]]
#> z()
#>
#> [[4]]
#> stop("asdf")

I'm a bit late to the party, but I found the best way was to use an exit handler in the function you are trying.
main <- function()
{
on.exit({
msg <- capture.output(traceback())
if (msg != "No traceback available ")
{
print(msg)
}
}
)
# rest of code
}
withCallingHandlers(
expr =
{
main()
},
error = function(e)
{
print(e)
}
)

Related

When debugging in RStudio, how to operate an object in another function in the stack?

I have a basic question with RStudio as following.
When I run the following R code in RStudio, it will pause at browser() in function f3(). At that point, I want to operate on the object i1 in function f1(). For example, I want to print(i1).
However, I found that I cannot do it. Does anyone know how to do it?
f1 <- function() {
i1 <- 1
f2()
}
f2 <- function() {
i2 <- 2
f3()
}
f3 <- function() {
i3 <- 3
browser()
}
f1()
Each level of function call comes with its own environment. You can access them with the parent.frame function.
Browse[1]> ls()
[1] "i3"
Browse[1]> ls(parent.frame())
[1] "i2"
Browse[1]> ls(parent.frame(2))
[1] "i1"
Browse[1]> ls(parent.frame(3))
[1] "f1" "f2" "f3"
The last one above is the global environment.
Then you can get or change a value:
Browse[1]> get("i1", envir = parent.frame(2))
[1] 1
Browse[1]> assign("i1", 10, envir = parent.frame(2))
Browse[1]> get("i1", envir = parent.frame(2))
[1] 10
Here is a function to get the value of a local variable in any parent frame:
getval <- function(name) {
i <- 0
repeat {
i <- i + 1
e <- parent.frame(i)
if (exists(name, envir = e)) return(get(name, envir = e))
if (identical(e, .GlobalEnv)) break
}
}
Browse[1]> getval("i3")
[1] 3
Browse[1]> getval("i2")
[1] 2
Browse[1]> getval("i1")
[1] 10
You may write an equivalent setval function if necessary.
If you only want to look at the values of other functions that have been called in the process of the script you can use the traceback functionality shown here on the bottom right:
If you select f1() there, you will see the value of i1 directly in the Environment view:

tryCatch and "promise already under evaluation" [duplicate]

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.
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)
Try the evaluate package.
library(evaluate)
test <- function(i)
switch(i, "1"=stop("oops"), "2"={ warning("hmm"); i }, i)
t1 <- evaluate("test(1)")
t2 <- evaluate("test(2)")
t3 <- evaluate("test(3)")
It currently lacks a nice way of evaluating expression though - this is mainly because it's targetted towards reproducing exactly what R output's given text input at the console.
replay(t1)
replay(t2)
replay(t3)
It also captures messages, output to the console, and ensures that everything is correctly interleaved in the order in which it occurred.
I have merged Martins soulution (https://stackoverflow.com/a/4952908/2161065) and the one from the R-help mailing list you get with demo(error.catching).
The main idea is to keep both, the warning/error message as well as the command triggering this problem.
myTryCatch <- function(expr) {
warn <- err <- NULL
value <- withCallingHandlers(
tryCatch(expr, error=function(e) {
err <<- e
NULL
}), warning=function(w) {
warn <<- w
invokeRestart("muffleWarning")
})
list(value=value, warning=warn, error=err)
}
Examples:
myTryCatch(log(1))
myTryCatch(log(-1))
myTryCatch(log("a"))
Output:
myTryCatch(log(1))
# $value
# [1] 0
#
# $warning
# NULL
#
# $error
# NULL
myTryCatch(log(-1))
# $value
# [1] NaN
#
# $warning
# <simpleWarning in log(-1): NaNs produced>
#
# $error
# NULL
myTryCatch(log("a"))
# $value
# NULL
#
# $warning
# NULL
#
# $error
# <simpleError in log("a"): non-numeric argument to mathematical function>
The purpose of my answer (and modification to Martin's excellent code) is so that the factory-ed function returns the data structure expected if all goes well. If a warning is experienced, it is attached to the result under the factory-warning attribute. data.table's setattr function is used to allow for compatibility with that package. If an error is experienced, the result is the character element "An error occurred in the factory function" and the factory-error attribute will carry the error message.
#' Catch errors and warnings and store them for subsequent evaluation
#'
#' Factory modified from a version written by Martin Morgan on Stack Overflow (see below).
#' Factory generates a function which is appropriately wrapped by error handlers.
#' If there are no errors and no warnings, the result is provided.
#' If there are warnings but no errors, the result is provided with a warn attribute set.
#' If there are errors, the result retutrns is a list with the elements of warn and err.
#' This is a nice way to recover from a problems that may have occurred during loop evaluation or during cluster usage.
#' Check the references for additional related functions.
#' I have not included the other factory functions included in the original Stack Overflow answer because they did not play well with the return item as an S4 object.
#' #export
#' #param fun The function to be turned into a factory
#' #return The result of the function given to turn into a factory. If this function was in error "An error as occurred" as a character element. factory-error and factory-warning attributes may also be set as appropriate.
#' #references
#' \url{http://stackoverflow.com/questions/4948361/how-do-i-save-warnings-and-errors-as-output-from-a-function}
#' #author Martin Morgan; Modified by Russell S. Pierce
#' #examples
#' f.log <- factory(log)
#' f.log("a")
#' f.as.numeric <- factory(as.numeric)
#' f.as.numeric(c("a","b",1))
factory <- function (fun) {
errorOccurred <- FALSE
library(data.table)
function(...) {
warn <- err <- NULL
res <- withCallingHandlers(tryCatch(fun(...), error = function(e) {
err <<- conditionMessage(e)
errorOccurred <<- TRUE
NULL
}), warning = function(w) {
warn <<- append(warn, conditionMessage(w))
invokeRestart("muffleWarning")
})
if (errorOccurred) {
res <- "An error occurred in the factory function"
}
if (is.character(warn)) {
data.table::setattr(res,"factory-warning",warn)
} else {
data.table::setattr(res,"factory-warning",NULL)
}
if (is.character(err)) {
data.table::setattr(res,"factory-error",err)
} else {
data.table::setattr(res, "factory-error", NULL)
}
return(res)
}
}
Because we don't wrap the result in an extra list we can't make the kind of assumptions that allow for some of his accessor functions, but we can write simple checks and decide how to handle the cases as is appropriate to our particular resulting data-structure.
.has <- function(x, what) {
!is.null(attr(x,what))
}
hasWarning <- function(x) .has(x, "factory-warning")
hasError <- function(x) .has(x, "factory-error")
isClean <- function(x) !(hasError(x) | hasWarning(x))

Invoke browser in parent context

Let's assume I have the following code:
maybeBrowser <- function (msg) {
if (interactive()) {
cat(msg, "\n")
???
} else {
stop(msg)
}
}
foo <- function (cond, ...) {
if (cond) maybeBrowser("What a mess")
}
What would ??? have to look like in order to invoke browser() in the context of foo if cond evaluates to TRUE?
Perhaps not the most elegant, but this seems to do what I think you're asking for.
First, two notes:
I'm adding browser(); 1, knowing it doesn't work right away. The ;1 is because browser() will exit immediately if there is not some code after it. If there's something after the if/else block then you might not need it, but it's there for this. (This is only necessary with emacs/ESS: https://github.com/emacs-ess/ESS/issues/178)
I added a variable within the foo environment to demonstrate that we don't (then do) see it.
First, the failing attempt:
maybeBrowser <- function (msg) {
if (interactive()) {
cat(msg, "\n")
browser()
q
} else {
stop(msg)
}
}
foo <- function (cond, ...) {
cat(capture.output(environment()), "\n")
in_foo <- 1
if (cond) maybeBrowser("What a mess")
}
foo(TRUE)
# <environment: 0x000000001b2beba0>
# What a mess
# Called from: maybeBrowser("What a mess")
# Browse[1]>
debug at #5: q
# Browse[2]>
environment()
# <environment: 0x000000001b280030> <---- this is different
# Browse[2]>
ls()
# [1] "msg"
Now a tweak to the code, motivated by https://stackoverflow.com/a/23891089/3358272
maybeBrowser <- function (msg) {
if (interactive()) {
cat(msg, "\n")
return(evalq(browser(skipCalls=1), envir=parent.frame()))
} else {
stop(msg)
}
}
foo <- function (cond, ...) {
cat(capture.output(environment()), "\n")
in_foo <- 1
if (cond) maybeBrowser("What a mess")
}
foo(TRUE)
# <environment: 0x000000001b0b9d40>
# What a mess
# Called from: eval(quote({
# browser()
# 1
# ...
# Browse[1]>
debug at #4: [1] 1
# Browse[3]>
environment()
# <environment: 0x000000001b0b9d40> <---- this is now the same
# Browse[3]>
ls()
# [1] "cond" "in_foo"
However, this is not allowing you to continue, stepping through any following code in foo, so it is an incomplete answer. I think unfortunately that that may not be feasible ... but perhaps a more internals-cognizant R bubba will have more clarity on this.

Run a function in tryCatch in case of error/warning

I'm using a tryCatch function in which I want another function to be run in case of error/warning. This other function depends on some arguments and for some reason, tryCatch does not recognize them when they are the error and warning functions.
Here is a simplified function where I'm facing the same problem:
essai <- function(x){
y <- 2
result <- tryCatch({
sqrt(x*y)
} , warning = function(cond,x,y) {
message(cond)
sqrt(abs(x*y))
} , error = function(cond,x,y) {
message(cond)
sqrt(abs(x*y))
} , finally = {
message("done")
} )
}
nbs <- c(1,2,3,-1,-2)
lapply(nbs, essai)
But here I have an error message saying Error in value[[3L]](cond) : argument "x" is missing, with no default. R doesn't understand it has to reuse x and y used in the failed function, why?
error and warning take functions of one argument. This works fine:
essai <- function(x, cond = 'problem'){
y <- 2
result <- tryCatch({
sqrt(x*y)
} , warning = function(w) {
message(cond)
sqrt(abs(x*y))
} , error = function(e) {
message(cond)
sqrt(abs(x*y))
} , finally = {
message("done")
} )
}
The rest of the arguments can be specified in your essai function and will be available from both warning and error.
nbs <- c(1,2,3,-1,-2)
lapply(nbs, essai)
#done
#done
#done
#problem
#done
#problem
#done
# [[1]]
# [1] 1.414214
#
# [[2]]
# [1] 2
#
# [[3]]
# [1] 2.44949
#
# [[4]]
# [1] 1.414214
#
# [[5]]
# [1] 2

R: promise already under evaluation

I understand that you are probably sick and tired of answering the same question again, but I am still getting the error discussed in several other questions:
promise already under evaluation: recursive default argument reference or earlier problems?
even though I did follow the "cumbersome" advice of prepending ".":
show.large.objects.threshold <- 100000
show.large.objects.exclude <- c("closure")
show.large.objects <- function (.envir = sys.frame(),
threshold = show.large.objects.threshold,
exclude = show.large.objects.exclude) {
for (n in print(ls(.envir, all.names = TRUE))) tryCatch({
o <- get(n,envir = .envir)
s <- object.size(o)
if (s > threshold && !(typeof(o) %in% exclude)) {
cat(n,": ")
print(s,units="auto")
}
}, error = function(e) { cat("n=",n,"\n"); print(e) })
}
show.large.objects.stack <- function (.threshold = show.large.objects.threshold,
skip.levels = 1,# do not examine the last level - this function
.exclude = show.large.objects.exclude) {
for (level in 1:(sys.nframe()-skip.levels)) {
cat("*** show.large.objects.stack(",level,") ")
print(sys.call(level))
show.large.objects(.envir = sys.frame(level), threshold = .threshold, exclude = .exclude)
}
}
but I still get errors:
> f <- function () { c <- 1:1e7; d <- 1:1e6; print(system.time(show.large.objects.stack())) }
> f()
*** show.large.objects.stack( 1 ) f()
[1] "c" "d"
c : 38.1 Mb
d : 3.8 Mb
*** show.large.objects.stack( 2 ) print(system.time(show.large.objects.stack()))
[1] "..." "x"
n= ...
<simpleError in get(n, envir = .envir): argument "..." is missing, with no default>
n= x
<simpleError in get(n, envir = .envir): promise already under evaluation: recursive default argument reference or earlier problems?>
*** show.large.objects.stack( 3 ) system.time(show.large.objects.stack())
[1] "expr" "gcFirst" "ppt" "time"
n= expr
<simpleError in get(n, envir = .envir): promise already under evaluation: recursive default argument reference or earlier problems?>
user system elapsed
0 (0.00ms) 0 (0.00ms) 0.002 (2.00ms)
So, what am I still doing wrong?
Do I really need the . in .envir? What about .exclude and .threshold?
Why do I get the argument "..." is missing, with no default error?
Why do I get the promise already under evaluation error?
Thanks!
When f is called a stack of 5 levels is built down to show.large.objects, which starts to evaluate the contents of the frames starting from the top.
f
-> print
-> system.time
-> show.large.objects.stack
-> show.large.objects
Level 1
f()
Everything ok here.
Level 2
print(system.time(show.large.objects.stack()))
When you call ls(.envir, all.names) on its frame you get
[1] "..." "x"
of which ... is missing and throws error 3 when you call get on it, and x = system.time(show.large.objects.stack()) is currently being evaluated and throws error 4.
Level 3
system.time(show.large.objects.stack())
whose ls gives you
[1] "expr" "gcFirst" "ppt" "time"
of which expr = show.large.objects.stack() is still currently being evaluated and throws another of error 4.
Level 4
show.large.objects.stack()
whose ls contain no sketchy things and completes without errors.
Bottom line
show.large.frames() must be evalutad on its own, not as an argument to any function, or it will throw errors. Why not letting it do the printing itself?
I found this very helpful
> debug(show.large.objects)
> f()
Browse[2]> lapply(sys.frames(), ls)
[[1]]
[1] "c" "d"
[[2]]
[1] "x"
[[3]]
[1] "expr" "gcFirst" "ppt" "time"
[[4]]
[1] "level" "skip.levels"
[[5]]
[1] "exclude" "threshold"

Resources