Handle more than one possible error in tryCatch - r

I'm trying to handle two possible errors in a for loop, which calls stplanr::dist_google to interact with an API. I know the errors, so I want to take specific actions when they occur.
If I try to handle just one of the possible errors, it works:
data(cents, package = "stplanr")
data(flow, package = "stplanr")
od <- stplanr::od2odf(flow=flow, zones=cents)
uma_linha <- data.frame(from_addresses=NA, to_addresses=NA, distances=NA,
duration=NA, currency=NA, fare=NA)
output <- data.frame()
for (linha in 1:nrow(od)) {
o <- od[linha, 3:4]
d <- od[linha, 5:6]
output <- tryCatch(
{
rbind(output, stplanr::dist_google(from = o, to = d,
mode = 'walking'))
},
error = function(na) {
message("Erro: No results for this request (e.g. due to lack of support for this mode between the from and to locations)")
message(na)
output <- rbind(output, uma_linha)
}
)
}
I need to get results for more than 2500 observations. Then, to automatize the use of two apis. I tried to include the other message and a little trick as an action. When I call the loop, I get the two error messages repeatedly.
n <- 1
apis <- c("api01", "api02", "api03")
for (linha in 1:nrow(od)) {
o <- od[linha, 3:4]
d <- od[linha, 5:6]
output <- tryCatch(
{
rbind(output, stplanr::dist_google(from = o, to = d,
mode = 'walking',
google_api = apis[n]))
},
error = function(na) {
message("Erro: No results for this request (e.g. due to lack of support for this mode between the from and to locations)")
message(na)
output <- rbind(output, uma_linha)
},
error = function(quota) {
message("Erro: You have exceeded your daily request quota for this API.")
message(quota)
n <- n + 1
return(n)
}
)
}
I'm confused with tryCatch. What am I doing wrong?
Many thanks for any help.
EDIT
After Martin's clear explanation, I tried to put everything inside a function. It's not working.
If the error is:
No results for this request (e.g. due to lack of support for this mode between the from and to locations)
then the function will go ahead and return an empty object. When the quota ends and the error is:
You have exceeded your daily request quota for this API.
it returned an error recursively, instead of performing n <- n + 1 and continued looping:
asha_dists <- function(fluxo, zonas, api) {
zonas <- as(st_transform(zonas, 4326), "Spatial")
od <- stplanr::od2odf(flow = fluxo, zones = zonas)
uma_linha <- data.frame(from_addresses=NA, to_addresses=NA, distances=NA,
duration=NA, currency=NA, fare=NA)
n <- 1
output <- data.frame()
for (linha in 1:nrow(od)) {
o <- od[linha, 3:4]
d <- od[linha, 5:6]
output <- tryCatch(
{
rbind(output, stplanr::dist_google(from = o, to = d,
mode = 'walking', google_api = api[n]))
},
custom_error = function(e) {
err <- conditionMessage(e)
message("found custom_error: ", err)
output <- rbind(output, uma_linha)
},
error = function(e) {
err <- conditionMessage(e)
message("found an error: ", err)
n <- n + 1
}
)
}
return(output)
}
Sent this request: https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=-23.5678377804732,-46.5708261676873&destinations=-23.5706647015703,-46.5755950203842&mode=walking&key=AIzaSyBRPrAjSE_pRMWSq_XlO4BFwGD63j_gB4U
found an error: You have exceeded your daily request quota for this API.
Sent this request: https://maps.googleapis.com/maps/api/distancematrix/json?units=metric&origins=-23.5665596480444,-46.5682308348154&destinations=-23.5706647015703,-46.5755950203842&mode=walking&key=AIzaSyBRPrAjSE_pRMWSq_XlO4BFwGD63j_gB4U
found an error: You have exceeded your daily request quota for this API.
I'm simply stuck.

There are two ways to catch types of errors, and it depends on how the error is generated. Here's a function that generates errors
f <- function(type) {
switch(
type,
a = stop("oops a: type 1 error"),
b = stop("oops b: type 2 error"),
c = {
err <- simpleError("oop 2: custom error class")
class(err) <- c("custom_error", class(err))
stop(err)
}
)
}
If type is "a" or "b", then the function generates a standard error condition but with different condition messages ("oops a: ..." and "oops b: ..."). If type is "c", then the error has a particular class "custom_error", that extends the (S3) class of a standard error.
> f("a")
Error in f("a") : oops a: type 1 error
> f("b")
Error in f("b") : oops b: type 2 error
> f("c")
Error: oop 2: custom error class
Here's a function that catches errors.
g <- function(type) {
tryCatch({
f(type)
}, custom_error = function(e) {
err <- conditionMessage(e)
message("found custom_error: ", err)
}, error = function(e) {
err <- conditionMessage(e)
if (startsWith(err, "oops a")) {
message("found type 'a' error: ", err)
} else if (startsWith(err, "oops b")) {
message("found type 'b' error: ", err)
} else {
message("generic error: ", err)
}
})
}
The handler arguments to tryCatch are tested in the order they occur in the argument list, and they are evaluated if they match the class of the error. Errors of type "a" or "b" have the same class, so are caught by the same handler; the only option to differentiate between them is to 'scrape' the error message looking at the condition (error) message to determine the type of error one is dealing with (hopefully the package isn't so sophisticated as to include error message translation, like base R does, because it is generally not possible to scrape translated errors in a robust way...)
> g("a")
found type 'a' error: oops a: type 1 error
> g("b")
found type 'b' error: oops b: type 2 error
On the other hand, type "c" can be caught by the handler because it has it's own class. So...
> g("c")
found custom_error: oop 2: custom error class
One can actually pass errors along the error handlers or up the call stack
h <- function(type) {
tryCatch({
f(type)
}, custom_error = function(e) {
err <- conditionMessage(e)
message("found custom_error: ", err)
stop(e)
}, error = function(e) {
err <- conditionMessage(e)
message("found an error: ", err)
stop(e)
})
}
with
> h("c")
found custom_error: oop 2: custom error class
found an error: oop 2: custom error class
Error: oop 2: custom error class
Few packages actually use the ability to make custom errors, so you're probably stuck with trying to scrape the errors that you do see. For your case, it seems like you have to scrape, so
output <- tryCatch({
rbind(
output,
stplanr::dist_google(
from = o, to = d, mode = 'walking', google_api = api[n]
)
)
}, error = function(e) {
err <- conditionMessage(e)
if (startsWith("No results for this request", err) {
warning(err) # warn instead of error
n <<- n + 1 # '<<-' to update n _outside_ this function
rbind(output, uma_linha) # empty output as result
} else if (startsWith("You have exceeded your daily", err) {
stop(e) # signal the original error
} else {
stop(e) # no error that we know how to handle, signal
}
})

Related

How to skip the error file and continue to read the next one when batch reading files in R [duplicate]

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.

Is it possible to handle simple messages in R? If yes, how?

To handle warnings or errors one can use
result = tryCatch({
expr
}, warning = function(w) {
warning-handler-code
}, error = function(e) {
error-handler-code
}, finally = {
cleanup-code
}
but if expr gives a message through simpleMessage, how can I get it? Is there something like?:
message = function(m) {message-handler-code}
Or another function which allows me to capture the message?
Thank you!
Yes, you can use message = just as you can with warning and error:
result = tryCatch({
message("Hello world")
1 + 1
}, message = function(m) {m}
)
result
#> <simpleMessage in message("Hello world"): Hello world
>
It's more likely however that you would want to capture your result and message separately:
result <- withCallingHandlers({
message("Hello world")
1 + 1
}, message = function(m) {
lastMessage <<- m
invokeRestart("muffleMessage")
})
result
#> [1] 2
if(exists("lastMessage")) message(lastMessage)
#> Hello world
tryCatch is the most commonly useful solution for handling conditions.
However, tryCatch aborts execution and returns the value of the handler, rather than resuming execution of the code. This may not always be what you want; sometimes you want to handle a condition and carry on.
R allows this thanks to its incredibly powerful condition system.
For example, you can choose to silence all messages:
suppressMessages(expr)
The nice thing here is that the suppressMessages isn’t magic — it’s a plain old R function and you could write it yourself (but you do need to understand the condition system, and the price for its versatility is that it’s fairly complicated).
To illustrate this, here’s another way of handling messages — using withCallingHandlers — which suppresses the messages, carries on executing the code it’s called with, but at the same time logs the message in a list:
messages = list()
withCallingHandlers({
message('Hello world')
1 + 1
}, message = function (msg) {
messages <<- c(messages, msg)
tryInvokeRestart('muffleMessage')
})
tryInvokeRestart('muffleMessage') is the same method by which suppressMessages works. The only difference is that we added code to store the message.
As a last step, the above can even be wrapped inside a function:
with_logging = function (expr) {
messages = list()
log_message = function (msg) {
messages <<- c(messages, conditionMessage(msg))
tryInvokeRestart('muffleMessage')
}
result = withCallingHandlers(expr, message = log_message)
list(result = result, messages = messages)
}
And to use it:
with_logging({
message('this is a test')
1 + 1
})
$result
[1] 2
$messages
$messages[[1]]
[1] "this is a test\n"

Specify the calling function for an error message in R

I'm working on an R package where the same input-checking functions are called by multiple "actual" functions that are exported to users. If I use a simple stop() call, the error message is going to say that an error occurred in the input-checking function, which is not that useful...
I thought I'd get around this by wrapping the call to the input-checking function inside a tryCatch(), and then handling the error in the outer function. This does mostly what I want, but doesn't quite give the output that I'd like. The closest I've come is the following:
f <- function(i) {
tryCatch({
check_input(i)
}, error = function(e) stop("in f: ", e$message, call. = FALSE)
)
}
check_input <- function(i) {
if(i < 0)
stop("i is negative, value given was ", i)
}
f(-1)
# Error: in f: i is negative, value given was -1
Ideally, I'd like the error message to be
Error in f: i is negative, value given was -1
, which would be the case if stop were called within f() instead of check_input().
Here's how you can grab the name of the function from the call stack and paste it in to the error message
f <- function(i) {
check_input(i)
}
g <- function(i) {
check_input(i)
}
check_input <- function(i, from=deparse(sys.calls()[[sys.nframe()-1]][[1]])) {
getmsg <- function(m) ifelse(!is.null(from), paste0("in ", from, ": ", m), m)
if(i < 0)
stop(getmsg(paste0("i is negative, value given was ", i)), call. = FALSE)
}
f(-1)
# Error: in f: i is negative, value given was -1
g(-1)
# Error: in g: i is negative, value given was -1
You could also call check_input(i, from="otherfunction") to show whatever function name you want or check_input(i, from=NULL) to suppress the function name.

How to show error location in tryCatch?

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))
})

Do loop again when error

I tried to read everything, but i kind of got stuck on one problem.
By using bigrquery I create queries to Google BigQuery to get data - unfortunately sometimes my query doesn't work because of a time-out.
Q is a SQL-Query and BQ is supposed to store the data downloaded from BigQuery.
Does anybody know how to re-do a loop every time tryCatch gives me an error?
I got this so far:
BQ_Foo <- NULL
tryCatch(
{
repeat{
BQ_Foo <- query_exec(Q_foo,"bigquery")
if(is.list(BQ_Foo) == TRUE)break }
}
,error=function(e){cat("ERROR : Query not loaded!", "\n")}
)
EDIT:
I tried my first approach again and this time i received this error message:
Error in curl::curl_fetch_memory(url, handle = handle) :
Operation was aborted by an application callback
Does anybody know how to handle this?
Widely based on r2evans answer, here's how to do the same kind of things with withRestarts, with some helps from This blog post:
set.seed(2)
foo <- NULL
operation <- function(x,tries) {
message(paste("x is",x,"remaining tries",tries))
withRestarts(
tryCatch({
if (runif(1) < x) stop("fail!") else 1
},
error=function(e) { invokeRestart("retry")}),
retry = function() {
message("Retrying")
stopifnot(tries > 0)
operation(x,tries-1)
}
)
}
> operation(0.9,5)
# x is 0.9 remaining tries 5
# Retrying
# x is 0.9 remaining tries 4
# Retrying
# x is 0.9 remaining tries 3
# Retrying
# x is 0.9 remaining tries 2
# Retrying
# x is 0.9 remaining tries 1
[1] 1
It's a kind of recursive call, so you can do whatever you want before calling the function again.
You may do it in the tryCatch error handler the same way, the interest to use restarts handlers is to call a specific function, if you had two tryCatch for which you want nearly same handler behavior then you can add a parameter and use the same handler for each try catch, i.e.:
testfun <- function(x) {
withRestarts({
tryCatch(
{
ifelse(runif(1) < 0.5,stop("Error Message"),warning("Warning message"))
},
warning=function(e) { invokeRestart("logger", level="warning", message=e ) },
error=function(e) { invokeRestart("logger", level="error", message=e ) }
)
},
logger = function(level,message) {
message(date()," [",level,"]: ",message[['message']])
}
)
}
Giving:
> set.seed(2)
> testfun()
Fri Jul 29 14:15:11 2016 [error]: Error Message
> testfun()
Fri Jul 29 14:15:12 2016 [warning]: Warning message
> testfun()
Fri Jul 29 14:15:13 2016 [warning]: Warning message
> testfun()
Fri Jul 29 14:15:13 2016 [error]: Error Message
Main interest here is the factorizing of the logger method and to reduce code duplication.
Naïve Solution
You might start with a mildly naïve attempt of putting the repeat/while outside the tryCatch, something like this:
set.seed(2)
foo <- NULL
while (is.null(foo)) {
foo <- tryCatch({
if (runif(1) < 0.9) stop("fail!") else 1
},
error = function(e) { message("err"); NULL; }
)
}
# err
# err
# err
# err
message("success: ", foo)
# success: 1
Unfortunately you introduce the possibility that the loop will never return. To protect against this, you can try a counter ...
Less-Naïve Solution
set.seed(2)
foo <- NULL
max_attempts <- 3
counter <- 0
while (is.null(foo) && counter < max_attempts) {
counter <- counter + 1
foo <- tryCatch({
if (runif(1) < 0.9) stop("fail!") else 1
},
error = function(e) { message("err"); NULL; }
)
}
# err
# err
# err
if (is.null(foo)) message("final failure") else message("success: ", foo)
# final failure
Now this is better for you, but it may still inadvertently introduce a denial-of-service "attack" on the server. (Consider "why" the query failed: if it is because the server is temporarily inundated, then you are making things worse by clobbering it even for a few limited requests.) Though it slows you down a little, in the case of a busy server, putting in pauses will ease the burden on the server and possibly give you a better chance of a successful query before failing.
Better Solution
In network parlance, small TCP packets can cause congestion when repeated retried (see Nagle's Algorithm for a quick reference). Using some form of exponential backoff is common, and to guard against two (or more) clients doing exactly same backoff simultaneously, some clients jitter slightly (for example, httr::RETRY).
set.seed(2)
foo <- NULL
max_attempts <- 3
# borrowed from hadley/httr::RETRY
pause_cap <- pause_base <- 1
counter <- 0
while (is.null(foo) && counter < max_attempts) {
if (counter > 0L) {
length <- stats::runif(1, max = min(pause_cap, pause_base * (2 ^ counter)))
message("sleeping ", round(length, 1))
Sys.sleep(length)
}
counter <- counter + 1
foo <- tryCatch({
if (runif(1) < 0.9) stop("fail!") else 1
},
error = function(e) { message("err"); NULL; }
)
}
# err
# sleeping 0.7
# err
# sleeping 0.2
if (is.null(foo)) message("final failure") else message("success: ", foo)
# success: 1
Wrap-Up
Somewhat sloppy code, but I hope you get the point. Putting loops on network queries without some form of self-limit can very easily escalate into an inadvertent DOS.
Based on your ideas i created this code, which seems to work - i just need to stresstest it.
QFoo <- paste0('SQL Code', dateBQ, ' ')
BQFoo <- NULL
testfun <- function(QFoo) {
withRestarts({
tryCatch({
query_exec(QFoo, "bigquery")
},
warning = function(e) { invokeRestart("logger", level="warning", message = e) },
error = function(e) { invokeRestart("logger", level="error", message = e) })
},
logger = function(level, message) {
message(date(), " [", level, "]: ", message[['message']])
})
}
testfun(QFoo)

Resources