Skip to content

BatchtoolsParam fails to propagate errors in bpiterate #257

@DarwinAwardWinner

Description

@DarwinAwardWinner

I've discovered a case where BatchtoolsParam behaves differently from other backends. Consider the following reprex:

library(BiocParallel)
library(iterators)
library(assertthat)
library(testthat)
## Convert a foreach iterator into a BiocParallel iterator
makeIter <- function(it) {
  f <- function() {
    tryCatch(it$nextElem(), error = function(e) {
      if (e$message == "StopIteration") {
        NULL
      } else {
        stop(e)
      }
    })
  }
  f
}

## Example non-error usage of makeIter
bpiterate(
  makeIter(icount(10)),
  sqrt,
  BPPARAM = SerialParam()
)
#> [[1]]
#> [1] 1
#> 
#> [[2]]
#> [1] 1.414214
#> 
#> [[3]]
#> [1] 1.732051
#> 
#> [[4]]
#> [1] 2
#> 
#> [[5]]
#> [1] 2.236068
#> 
#> [[6]]
#> [1] 2.44949
#> 
#> [[7]]
#> [1] 2.645751
#> 
#> [[8]]
#> [1] 2.828427
#> 
#> [[9]]
#> [1] 3
#> 
#> [[10]]
#> [1] 3.162278

## Correctly throws the error
expect_error(bpiterate(
  makeIter(iterators::icount(10)),
  function(x) stop("This function always throws an error"),
  BPPARAM = SerialParam()
))

## Correctly throws the error
expect_error(bpiterate(
  makeIter(iterators::icount(10)),
  function(x) stop("This function always throws an error"),
  BPPARAM = MulticoreParam(workers = 2)
))

## Correctly throws the error
expect_error(bpiterate(
  makeIter(iterators::icount(10)),
  function(x) stop("This function always throws an error"),
  BPPARAM = SnowParam(workers = 2)
))

## Does not throw the error, but collects in attr(,"errors")
resList <- bpiterate(
  makeIter(iterators::icount(10)),
  function(x) stop("This function always throws an error"),
  BPPARAM = BatchtoolsParam(cluster = "socket", workers = 2)
)
#> Submitting 10 jobs in 2 chunks using cluster functions 'Socket' ...

print(resList)
#> [[1]]
#> NULL
#> 
#> [[2]]
#> NULL
#> 
#> [[3]]
#> NULL
#> 
#> [[4]]
#> NULL
#> 
#> [[5]]
#> NULL
#> 
#> [[6]]
#> NULL
#> 
#> [[7]]
#> NULL
#> 
#> [[8]]
#> NULL
#> 
#> [[9]]
#> NULL
#> 
#> [[10]]
#> NULL
#> 
#> attr(,"errors")
#> attr(,"errors")$`10`
#> <unevaluated_error: not evaluated due to previous error>
#> 
#> attr(,"errors")$`1`
#> <remote_error in FUN(...): This function always throws an error>
#> traceback() available as 'attr(x, "traceback")'
#> 
#> attr(,"errors")$`2`
#> <unevaluated_error: not evaluated due to previous error>
#> 
#> attr(,"errors")$`3`
#> <unevaluated_error: not evaluated due to previous error>
#> 
#> attr(,"errors")$`4`
#> <unevaluated_error: not evaluated due to previous error>
#> 
#> attr(,"errors")$`5`
#> <remote_error in FUN(...): This function always throws an error>
#> traceback() available as 'attr(x, "traceback")'
#> 
#> attr(,"errors")$`6`
#> <unevaluated_error: not evaluated due to previous error>
#> 
#> attr(,"errors")$`7`
#> <unevaluated_error: not evaluated due to previous error>
#> 
#> attr(,"errors")$`8`
#> <unevaluated_error: not evaluated due to previous error>
#> 
#> attr(,"errors")$`9`
#> <unevaluated_error: not evaluated due to previous error>

## This assertion fails
assert_that(!any(bpok(resList)))
#> Error: !any(bpok(resList)) is not TRUE
## This assertion passes
assert_that(!any(bpok(attr(resList, "errors"))))
#> [1] TRUE

Created on 2023-07-07 with reprex v2.0.2

With any other backend (SerialParam, MulticoreParam, SnowParam), the bpiterate call throws an error (verified here by calling expect_error). However, BatchtoolsParam does not throw an error and instead returns a list with all NULL elements and an attribute "errors" containing the errors thrown during iteration. Furthermore, bpok says this object is totally fine. I would expect BatchtoolsParam to behave the same as the other backends here.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions