95

I've read a few other SO questions about tryCatch and cuzzins, as well as the documentation:

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?

smci
  • 32,567
  • 20
  • 113
  • 146
isomorphismes
  • 8,233
  • 9
  • 59
  • 70

5 Answers5

101

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
Andrie
  • 176,377
  • 47
  • 447
  • 496
  • Thank you for this answer. I originally had avoided `next` like you recommend, but as I thought of more and more errors that could possibly be caught, the code became a tangle of parentheses: as bad or worse than "programming" a series of conditionals inside an Excel cell. – isomorphismes Nov 12 '11 at 01:13
  • When I run `?for` the next line is a `+`. (requesting more input) – isomorphismes Nov 12 '11 at 01:15
  • @Andrie, what is the point of writing `error=function(e)` if the function is empty? Is that supposed to be shorthand for something else? – isomorphismes Nov 12 '11 at 01:17
  • 1
    My mistake. It should be `error=function(e) e`. This returns the error message. I have also added an explanation of this mechanism in the answer. – Andrie Nov 12 '11 at 07:49
  • Thanks for the edit @Andrie, that makes a little more sense. I will do some more reading about `calling handler`s. – isomorphismes Nov 12 '11 at 07:58
  • 1
    I'm curious why you prefer to avoid 'next' - whether old school or new school, that's typically considered good practice IME. Very very different from the "goto considered harmful" stuff, which is indeed quite harmful. But loop control operators can greatly simplify code and eliminate sentinel variables, which have a tendency to have nasty edge cases. And as @LaoTzu points out, you can save a lot of parens & braces. – Ken Williams Jun 05 '12 at 20:52
  • @KenWilliams You are indeed correct that it is my personal hangover from "GOTO is bad". I'm gradually getting rid of this notion... – Andrie Jun 06 '12 at 04:32
  • @Andrie Thanks for this. TryCatch is R works very differently than what we're taught in most programming languages. – chandler Jan 18 '13 at 01:29
91

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 }     
}
stevec
  • 41,291
  • 27
  • 223
  • 311
  • 14
    finally! so many convoluted explanations. but here you have simplified the whole process and in so doing, did a great service to the community. This should be the most upvoted answer! – treetopdewdrop Nov 08 '19 at 19:14
  • 5
    For those who are confused by the `<<-`, that is not a typo. `<<-` is the superassignment operator. It does the assignment in the enclosing environment. That is, in this case, the `skip_to_next` from the for loop is being assigned a value from within `tryCatch()`. See more [here](https://stackoverflow.com/questions/2628621/how-do-you-use-scoping-assignment-in-r). – Dunois May 23 '20 at 17:59
  • Your answer is also a bit confusing, because you call `next` at the end of the loop, which is quite unnecessary in your case :-) Also, no need to `<<-`, since you can return value from the error handler and it will become the return value of `tryCatch`. – Tomas Aug 03 '21 at 09:48
  • 3
    This answer is accurate, concise, and appreciated. – Jessica Burnett Mar 15 '22 at 15:22
  • @stevec, I'm trying to get this to work for a for loop I'm running on a custom function. Something I'm still not getting: what part of your example should the "meat" of my loop go in? – Abe Sep 05 '22 at 00:30
  • 1
    @Abe the code that could error should go in place of `print(b)`. If you have multiple lines, place it all between curly brackets. – stevec Sep 05 '22 at 01:57
8
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 = ""))
  
}
Kim
  • 4,080
  • 2
  • 30
  • 51
ewittry
  • 81
  • 1
  • 2
6

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))
mmann1123
  • 5,031
  • 7
  • 41
  • 49
5

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.
Community
  • 1
  • 1
isomorphismes
  • 8,233
  • 9
  • 59
  • 70