When a call exists of multiple lines, a potential error only includes the first line of match.call() resulting in some lost information and an incomplete sentence. A simple example:
#proper error message:
runif(n=1, k=5)
#incomplete error message:
runif(n=1, k={5})
What would be a way to get R to include the full call to the error message (maybe by collapsing the multiple lines or so)? I am mostly interested in using this in a tryCatch setting.
I had a go at investigating the error object in a tryCatch setting via:
tryCatch( runif(n=1,k={5}),
error = function(e) recover() )
And then selected the 4th environment (value[[3]](cond)) to examine e.
I noticed that e$call was:
Browse[1]> e$call
runif(n = 1, k = {
5
})
So it seems that the error message just uses that first line.
You can collapse all the lines together with:
Browse[1]> paste(deparse(e$call),collapse='')
[1] "runif(n = 1, k = { 5})"
So you could try something like:
tryCatch( runif(n=1,k={5}),
error = function(e) {
cat(sprintf('Error in %s: %s\n',
paste(deparse(e$call),collapse=''),
e$message))
} )
But this doesn't fix up the error message itself, just the call leading up to it:
Error in runif(n = 1, k = { 5}): unused argument(s) (k = {
So the 'Error in xxx' is complete, but the 'unused argument(s) xxx' is still not. It's a start, but not all the way there.
I'm not sure how to improve on this (and am also interested to know if it's possible).
Related
I've read a few other SO questions about tryCatch and cuzzins, as well as the documentation:
Exception handling in R
catching an error and then branching logic
How can I check whether a function call results in a warning?
Problems with Plots in Loop
but I still don't understand.
I'm running a loop and want to skip to next if any of a few kinds of errors occur:
for (i in 1:39487) {
# EXCEPTION HANDLING
this.could.go.wrong <- tryCatch(
attemptsomething(),
error=function(e) next
)
so.could.this <- tryCatch(
doesthisfail(),
error=function(e) next
)
catch.all.errors <- function() { this.could.go.wrong; so.could.this; }
catch.all.errors;
#REAL WORK
useful(i); fun(i); good(i);
} #end for
(by the way, there is no documentation for next that I can find)
When I run this, R honks:
Error in value[[3L]](cond) : no loop for break/next, jumping to top level
What basic point am I missing here? The tryCatch's are clearly within the for loop, so why doesn't R know that?
The key to using tryCatch is realising that it returns an object. If there was an error inside the tryCatch then this object will inherit from class error. You can test for class inheritance with the function inherit.
x <- tryCatch(stop("Error"), error = function(e) e)
class(x)
"simpleError" "error" "condition"
Edit:
What is the meaning of the argument error = function(e) e? This baffled me, and I don't think it's well explained in the documentation. What happens is that this argument catches any error messages that originate in the expression that you are tryCatching. If an error is caught, it gets returned as the value of tryCatch. In the help documentation this is described as a calling handler. The argument e inside error=function(e) is the error message originating in your code.
I come from the old school of procedural programming where using next was a bad thing. So I would rewrite your code something like this. (Note that I removed the next statement inside the tryCatch.):
for (i in 1:39487) {
#ERROR HANDLING
possibleError <- tryCatch(
thing(),
error=function(e) e
)
if(!inherits(possibleError, "error")){
#REAL WORK
useful(i); fun(i); good(i);
}
} #end for
The function next is documented inside ?for`.
If you want to use that instead of having your main working routine inside an if, your code should look something like this:
for (i in 1:39487) {
#ERROR HANDLING
possibleError <- tryCatch(
thing(),
error=function(e) e
)
if(inherits(possibleError, "error")) next
#REAL WORK
useful(i); fun(i); good(i);
} #end for
I found other answers very confusing. Here is an extremely simple implementation for anyone who wants to simply skip to the next loop iteration in the event of an error
for (i in 1:10) {
skip_to_next <- FALSE
# Note that print(b) fails since b doesn't exist
tryCatch(print(b), error = function(e) { skip_to_next <<- TRUE})
if(skip_to_next) { next }
}
for (i in -3:3) {
#ERROR HANDLING
possibleError <- tryCatch({
print(paste("Start Loop ", i ,sep=""))
if(i==0){
stop()
}
}
,
error=function(e) {
e
print(paste("Oops! --> Error in Loop ",i,sep = ""))
}
)
if(inherits(possibleError, "error")) next
print(paste(" End Loop ",i,sep = ""))
}
The only really detailed explanation I have seen can be found here: http://mazamascience.com/WorkingWithData/?p=912
Here is a code clip from that blog post showing how tryCatch works
#!/usr/bin/env Rscript
# tryCatch.r -- experiments with tryCatch
# Get any arguments
arguments <- commandArgs(trailingOnly=TRUE)
a <- arguments[1]
# Define a division function that can issue warnings and errors
myDivide <- function(d, a) {
if (a == 'warning') {
return_value <- 'myDivide warning result'
warning("myDivide warning message")
} else if (a == 'error') {
return_value <- 'myDivide error result'
stop("myDivide error message")
} else {
return_value = d / as.numeric(a)
}
return(return_value)
}
# Evalute the desired series of expressions inside of tryCatch
result <- tryCatch({
b <- 2
c <- b^2
d <- c+2
if (a == 'suppress-warnings') {
e <- suppressWarnings(myDivide(d,a))
} else {
e <- myDivide(d,a) # 6/a
}
f <- e + 100
}, warning = function(war) {
# warning handler picks up where error was generated
print(paste("MY_WARNING: ",war))
b <- "changing 'b' inside the warning handler has no effect"
e <- myDivide(d,0.1) # =60
f <- e + 100
return(f)
}, error = function(err) {
# warning handler picks up where error was generated
print(paste("MY_ERROR: ",err))
b <- "changing 'b' inside the error handler has no effect"
e <- myDivide(d,0.01) # =600
f <- e + 100
return(f)
}, finally = {
print(paste("a =",a))
print(paste("b =",b))
print(paste("c =",c))
print(paste("d =",d))
# NOTE: Finally is evaluated in the context of of the inital
# NOTE: tryCatch block and 'e' will not exist if a warning
# NOTE: or error occurred.
#print(paste("e =",e))
}) # END tryCatch
print(paste("result =",result))
One thing I was missing, which breaking out of for loop when running a function inside a for loop in R makes clear, is this:
next doesn't work inside a function.
You need to send some signal or flag (e.g., Voldemort = TRUE) from inside your function (in my case tryCatch) to the outside.
(this is like modifying a global, public variable inside a local, private function)
Then outside the function, you check to see if the flag was waved (does Voldemort == TRUE). If so you call break or next outside the function.
I'm writing my own package and check some of the inputs. When the inputs aren't in the required format I want to stop with an error. However, I want to add a custom message to the default error.
However, something like this has the problem that the indentation of the default and cutom error message doesn't look nice depending on the input. E.g. could be that the second line is indented by a few chars.
test <- function(x, y, z)
{
if (x != 1)
stop(paste0("oops!", "\n", "x is not 1!"))
}
test(x= 2, y = "this is just a test", z = "we need to make the line very long")
leads to:
Error in test(x = 2, y = "this is just a test", z = "we need to make the line very long") :
oops!
x is not 1!
See how the second line is indented by two characters.
So I thought I write a function this way:
create_stops <- function(error_location, error_message)
{
stop(error_location, "\n", error_message, call. = FALSE)
}
test <- function(x, y, z)
{
if (x != 1)
{
error_location <- match.call()
error_message <- paste0("oops!", "\n", "x is not 1!")
create_stops(error_location, error_message)
}
}
test(x= 2, y = "this is just a test", z = "we need to make the line very long")
which returns:
Error: test2this is just a testwe need to make the line very long
oops!
x is not 1!
So here the problem is that the match.call() part doesn't return the full message compared to using teh "normal" stop function.
Any idea how I can get the full function call?
Bonus: is there any way how I can prevent having to use the error_location = match.call() in my main test function and instead use match.call in the create_stops function, but it would return the parent function, i.e. "Error in test", not "Error in create_stops"?
I have two functions that construct R S3 objects. One object encapsulates the other. When the user passes a bad num argument to fun2() it throws the error assertion in fun2(). I think it would be more informative for the user to see the message in fun1(). What are the options for preserving the message raised in fun1() and raising that instead of the message in fun2()?
fun1 <- function(num) {
assert_that(num %% 2 == 0, msg = "num should be even")
structure(num,
class = "fun1-Class"
)
}
fun2 <- function(text, num) {
assert_that(class(num) == "fun1-Class", msg = "Bad Class")
structure(list(text, num),
class = "fun2-Class"
)
}
fun1(1.2)
# Throws error "num should be even"
x <- fun2("myText", fun1(1.2))
# Throws error "Bad Class"
Traceback is below
Error: Bad Class
3.
stop(assertError(attr(res, "msg")))
2.
assert_that(class(num) == "fun1-Class", msg = "Bad Class")
1.
fun2("text", fun1(1.2))
#aurèle answered this in the comments.
This happens because of the lazy evaluation of function parameters. force(num) will change the behavior and evaluate the first constructor.
I have a for loop in R:
for i in seq(1 , nrow(df)){
try(function(i),silent=T)
}
I would like to revise this so that if an error message shows up, the function repeats the for loop one more time. If it fails again -
message(paste("function failed twice for", i))
`
Is try() the proper function for this purpose?
I don't know any R but my guess is that you can do something like this:
errorCount = 0
for i in seq(1 , nrow(df)){
tryCatch(function(i),silent=T,
error = {errorCount +=1,
tryCatch(function(i), silent=T,
error = {errorCount +=1
print("2 times error")
tryCatch(function(i), silent=T,
error = {errorCount +=1
tryCatch(function(i), silent=T,
error = {print("3 times error: Did not repeat")
break})
} )
})
)
}
Sorry about my terrible code - I made it happen 3 times as you specified, but you'll have to fix the syntax. Other answers are recommended.
Displaying error locations with options(show.error.locations = TRUE) doesn't seem to work when handling exceptions with tryCatch. I am trying to display location of the error but I don't know how:
options(show.error.locations = TRUE)
tryCatch({
some_function(...)
}, error = function (e, f, g) {
e <<- e
cat("ERROR: ", e$message, "\nin ")
print(e$call)
})
If I then look at the variable e, the location doesn't seem to be there:
> str(e)
List of 2
$ message: chr "missing value where TRUE/FALSE needed"
$ call : language if (index_smooth == "INDEX") { rescale <- 100/meanMSI[plotbaseyear] ...
- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
If I don't trap the error, it is printed on the console along with source file and line number. How to do it with tryCatch?
Context
As noted by Willem van Doesburg, it is not possible to use the traceback() function to display where the error occured with tryCatch(), and to my knowledge there is currently no practical way to store the position of the error with base functions in R while using tryCatch .
The idea of a separate error handler
The possible solution I found consists of two parts, the main one is writing an error handler similar to that of Chrispy from "printing stack trace and continuing after error occurs in R" which produces a log with the position of the error.
The second part is capturing this output into a variable, similarly to what was suggested by Ben Bolker in "is it possible to redirect console output to a variable".
The call stack in R seems to be purged when an error is raised and then handled (I might be wrong so any information is welcomed), hence we need to capture the error while it is occuring.
Script with an error
I used an example from one of your previous questions regarding where and R error occured with the following function stored in a file called "TestError.R" which I call in my example bellow:
# TestError.R
f2 <- function(x)
{
if (is.null(x)) "x is Null"
if (x==1) "foo"
}
f <- function(x)
{
f2(x)
}
# The following line will raise an error if executed
f(NULL)
Error tracing function
This is the function I adapted form Chrispy's code as I mentionned above.
Upon execution, if an error is raised, the code underneath will print where the error occured, in the case of the above function, it will print :
"Error occuring: Test.R#9: f2(x)" and "Error occuring: Test.R#14: f(NULL)" meaning the error result from a trouble with the f(NULL) function at line 14 which references the f2() function at line 9
# Error tracing function
withErrorTracing = function(expr, silentSuccess=FALSE) {
hasFailed = FALSE
messages = list()
warnings = list()
errorTracer = function(obj) {
# Storing the call stack
calls = sys.calls()
calls = calls[1:length(calls)-1]
# Keeping the calls only
trace = limitedLabels(c(calls, attr(obj, "calls")))
# Printing the 2nd and 3rd traces that contain the line where the error occured
# This is the part you might want to edit to suit your needs
print(paste0("Error occuring: ", trace[length(trace):1][2:3]))
# Muffle any redundant output of the same message
optionalRestart = function(r) { res = findRestart(r); if (!is.null(res)) invokeRestart(res) }
optionalRestart("muffleMessage")
optionalRestart("muffleWarning")
}
vexpr = withCallingHandlers(withVisible(expr), error=errorTracer)
if (silentSuccess && !hasFailed) {
cat(paste(warnings, collapse=""))
}
if (vexpr$visible) vexpr$value else invisible(vexpr$value)
}
Storing the error position and the message
We call the script TestError.R above and capture the printed output in a variable, here called errorStorage with which we can deal later on or simply display.
errorStorage <- capture.output(tryCatch({
withErrorTracing({source("TestError.R")})
}, error = function(e){
e <<- e
cat("ERROR: ", e$message, "\nin ")
print(e$call)
}))
Hence we keep the value of e with the call and message as well as the position of the error location.
The errorStorage output should be as follow:
[1] "[1] \"Error occuring: Test.R#9: f2(x)\" \"Error occuring: Test.R#14: f(NULL)\""
[2] "ERROR: argument is of length zero "
[3] "in if (x == 1) \"foo\""
Hoping this might help.
You can use traceback() in the error handler to show the call stack. Errors in a tryCatch don't produce line numbers. See also the help on traceback. If you use your tryCatch statements defensively, this will help you narrow down the location of the error.
Here is a working example:
## Example of Showing line-number in Try Catch
# set this variable to "error", "warning" or empty ('') to see the different scenarios
case <- "error"
result <- "init value"
tryCatch({
if( case == "error") {
stop( simpleError("Whoops: error") )
}
if( case == "warning") {
stop( simpleWarning("Whoops: warning") )
}
result <- "My result"
},
warning = function (e) {
print(sprintf("caught Warning: %s", e))
traceback(1, max.lines = 1)
},
error = function(e) {
print(sprintf("caught Error: %s", e))
traceback(1, max.lines = 1)
},
finally = {
print(sprintf("And the result is: %s", result))
})