0

I've got a nasty error which only shows up when you ask R with geterrmessage() to actually display it. It also is no problem as long as you run the code in an interactive console session but when you call R non-interactively (in this case through a 3rd party software) this error results in the script being aborted.

Does anyone have an idea why the below code generates a (hidden) StopIteration error?

# Load the required packages
library(foreach)
library(parallel)
library(doParallel)

# ----- set-up cluster for parallel processing -----
ncl <- max(2,floor(detectCores()*0.75)) #number of cores
clst <- makePSOCKcluster(n=ncl) #create cluster for R
registerDoParallel(cl = clst) #register cluster

# ----- display latest error  -----
print(geterrmessage())

# ----- run nested foreach/for loop and stop cluster afterwards -----
t <- foreach (j=1:8) %dopar% {
        for (i in j:8) {
            print(paste("j",j,"i",i))
        }
    }
stopCluster(clst)

# ----- display latest error  -----
print(geterrmessage())

Thanks, Mark

3
  • 1
    I think that due to the way foreach is implemented, it will always throw a stopIteration when its done processing chunks, and although it handles that, by not complaining , yet it doesnt handle it in such a way that it wont be available to geterrmessage() to peek; you might try to file an issue with foreach to see if they can address that.
    – Nir Graham
    Commented Feb 20 at 17:46
  • 1
    in the mean time, you should be able to fearlessly foreach by using function wrappers like purrr::safely to handle any error silently, and you can go through unpacking what was right and what was wrong after foreach ended, rather than check for a single global error message.
    – Nir Graham
    Commented Feb 20 at 17:47
  • Thanks @NirGraham for your helpful comments! Not having experience with purrr::safely I was not able to apply the suggested workaround successfully so any hint would be highly appreciated but I already have a good starting point where to look so thanks again!
    – MarkH
    Commented Feb 21 at 15:49

2 Answers 2

1

try this black magic : .Internal(seterrmessage("")) idea sourced from stat.ethz.ch/pipermail/r-help/2006-February/087931.html

1

There are many ways of using foreach, and proceeding despite errors, and logging them; here is an example of one possible approach

library(foreach)
library(parallel)
library(doParallel)
library(purrr) # for safely()

# ----- set-up cluster for parallel processing -----
ncl <- max(2,floor(detectCores()*0.75)) #number of cores
clst <- makePSOCKcluster(n=ncl) #create cluster for R
registerDoParallel(cl = clst) #register cluster


do_stuff <- function(j) {
  
  # bomb ; break on j = 6 
  if(j==6) stop("Bad News")
  make_string <- ""
  for (i in j:8) {
    make_string <- paste0(make_string,paste("j",j,"i",i))
  }
  make_string
}

do_stuff(8) # fine
do_stuff(6) #  error 

do_stuff_safely <- purrr::safely(do_stuff,"")

do_stuff_safely(8) # fine
do_stuff_safely(6) #  error 


# ----- run nested foreach/for loop and stop cluster afterwards -----
t <- foreach (j=1:8) %dopar% {
  do_stuff_safely(j)
}
stopCluster(clst)

# here you have a list of 8 coupled lists, result and error

(results <- lapply(t,\(x)x$result))  # <-- see how 6 is blank
(errors <- lapply(t,\(x)x$error))  # <-- see how 6  has an error recorded

# 
names(errors) <- seq_along(errors)
(any_error <- Filter(\(x)!is.null(x),x = errors) ) 

One can throw a try around the foreach itself to look for errors in foreach i.e. outside any single safe function call example

t <- try(expr = {foreach (j=1:8) %dopar% {
  stop("Error in foreach itself")
  do_stuff_safely(j)
}})

inherits(t,"try-error")

try the above having having removed the stop() and t should no longer inherit from try-error

9
  • Thanks so much @NirGraham! But this catches any errors caused by a function inside the foreach loop yet it still produces a silent StopIteration error by foreach itself. So far I was not successful in trying to wrap the whole foreach loop with purrr::safely but I haven't given up yet... You think it's doable?
    – MarkH
    Commented Feb 22 at 7:54
  • 1
    I think that due to the way foreach is implemented, it will always throw a stopIteration when its done processing chunks. you could put a single try() aroung foreach itself to catch if the overall foreach failed ?
    – Nir Graham
    Commented Feb 22 at 11:43
  • 1
    I extended my answer at the bottom
    – Nir Graham
    Commented Feb 22 at 11:52
  • Thanks, wrapping it with try and adding the inherits(t,"try-error") produces FALSE. I still get StopIteration though when calling geterrmessage(), so it still fails when called from that 3rd party S/W non-interactively. "Fun" fact: when building the parallelization with future.apply instead I also get a silent error (all connections are in use) and I will ask if it is any easier to avoid that one in a separate thread.
    – MarkH
    Commented Feb 23 at 7:57
  • 1
    try this black magic : .Internal(seterrmessage("")) idea sourced from stat.ethz.ch/pipermail/r-help/2006-February/087931.html
    – Nir Graham
    Commented Feb 23 at 17:21

Not the answer you're looking for? Browse other questions tagged or ask your own question.