tidyverse / purrr

A functional programming toolkit for R
https://purrr.tidyverse.org/
Other
1.28k stars 272 forks source link

trycatch not working in map() #1048

Closed plukethep closed 1 year ago

plukethep commented 1 year ago

The following code works as expected:

    map(c(1,2,3),
        function(x){
          record <-tryCatch({
            completed <- read_csv("http://example/com/madeup.csv")
            return(completed)
          },
          error=function(cond) {
            message("CAUGHT", cond)
            return(0)
          })
        })

But if I add another message command to the error part of trycatch it errors out

    map(c(1,2,3),
        function(x){
          record <-tryCatch({
            completed <- read_csv("http://example/com/madeup.csv")
            return(completed)
          },
          error=function(cond) {
            message("CAUGHT")
            message(cond)
            return(0)
          })
        })

    CAUGHT
    Error in `map()`:
    ℹ In index: 1.
    Caused by error in `open.connection()`:
    ! Could not resolve host: example
    Run `rlang::last_error()` to see where the error occurred.
    Called from: signal_abort(cnd, .file)

Putting this trycatch in a for loop works fine:

    for(x in c(1,2,3)){
        record <-tryCatch({
            completed <- read_csv("http://example/com/madeup.csv")
            return(completed)
          },
          error=function(cond) {
            message("CAUGHT")
            message(cond)
            return(0)
          })
    }

See discussion here, which suggest's that the casting for the message(cond) is at fault

purrr 1.0.0

hadley commented 1 year ago

Could you please rework your reproducible example to use the reprex package ? That makes it easier to see both the input and the output, formatted in such a way that I can easily re-run in a local session.

plukethep commented 1 year ago
library(tidyverse)

map(c(1,2,3),
    function(x){
      record <-tryCatch({
        completed <- read_csv("http://example/com/madeup.csv")
        return(completed)
      },
      error=function(cond) {
        message("CAUGHT")
        message(cond)
        return(0)
      })
    })
#> CAUGHT
#> Error in `map()`:
#> ℹ In index: 1.
#> Caused by error in `open.connection()`:
#> ! Could not resolve host: example

#> Backtrace:
#>      ▆
#>   1. ├─purrr::map(...)
#>   2. │ └─purrr:::map_("list", .x, .f, ..., .progress = .progress)
#>   3. │   ├─purrr:::with_indexed_errors(...)
#>   4. │   │ └─base::withCallingHandlers(...)
#>   5. │   └─global .f(.x[[i]], ...)
#>   6. │     └─base::tryCatch(...)
#>   7. │       └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   8. │         └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>   9. │           └─value[[3L]](cond)
#>  10. │             └─base::message(cond)
#>  11. │               ├─base::withRestarts(...)
#>  12. │               │ └─base (local) withOneRestart(expr, restarts[[1L]])
#>  13. │               │   └─base (local) doWithOneRestart(return(expr), restart)
#>  14. │               └─base::signalCondition(cond)
#>  15. └─purrr (local) `<fn>`(`<smplErrr>`)
#>  16.   └─cli::cli_abort(c(i = "In index: {i}."), parent = cnd, call = error_call)
#>  17.     └─rlang::abort(...)

Created on 2023-01-18 with reprex v2.0.2

hadley commented 1 year ago

I simplified your example a little bit, but I don't see the problem. It looks like f() behaves the same way inside and outside of map():

library(purrr)

f <- function(i) {
  tryCatch(
    {
      stop("error")
    },
    error = function(cond) {
      message(cond)
      return(0)
    }
  )
}
f()
#> Error in doTryCatch(return(expr), name, parentenv, handler): error

map(c(1,2,3), f)
#> Error in `map()`:
#> ℹ In index: 1.
#> Caused by error in `doTryCatch()`:
#> ! error

Created on 2023-01-18 with reprex v2.0.2

This is generally what I'd expect based on my understanding of message(): you're passing an error condition to message which will resignal it, causing the error to be rethrown.

What are you trying to do?

plukethep commented 1 year ago

I'm trying to catch and display the error within a map function, I believe that it used to display message absolutely fine, but has stopped since I upgraded to purrr 1.0.0 I'm still getting for loops and the f() function displaying the error message without complaint, and the for loop finishes, whilst the map function dies when it meets the error, see below

library(purrr)

f <- function(){
  tryCatch({
    stop("error")
  },
  error=function(cond) {
    message(cond)
  })
}

for(x in c(1)){
  f()
}
#> Error in doTryCatch(return(expr), name, parentenv, handler): error

f()
#> Error in doTryCatch(return(expr), name, parentenv, handler): error

map(c(1),function(x){
  f()
})
#> Error in `map()`:
#> ℹ In index: 1.
#> Caused by error in `doTryCatch()`:
#> ! error

#> Backtrace:
#>      ▆
#>   1. ├─purrr::map(...)
#>   2. │ └─purrr:::map_("list", .x, .f, ..., .progress = .progress)
#>   3. │   ├─purrr:::with_indexed_errors(...)
#>   4. │   │ └─base::withCallingHandlers(...)
#>   5. │   └─global .f(.x[[i]], ...)
#>   6. │     └─global f()
#>   7. │       └─base::tryCatch(...)
#>   8. │         └─base (local) tryCatchList(expr, classes, parentenv, handlers)
#>   9. │           └─base (local) tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  10. │             └─value[[3L]](cond)
#>  11. │               └─base::message(cond)
#>  12. │                 ├─base::withRestarts(...)
#>  13. │                 │ └─base (local) withOneRestart(expr, restarts[[1L]])
#>  14. │                 │   └─base (local) doWithOneRestart(return(expr), restart)
#>  15. │                 └─base::signalCondition(cond)
#>  16. └─purrr (local) `<fn>`(`<smplErrr>`)
#>  17.   └─cli::cli_abort(c(i = "In index: {i}."), parent = cnd, call = error_call)
#>  18.     └─rlang::abort(...)

Created on 2023-01-18 with reprex v2.0.2

hadley commented 1 year ago

It doesn't look like the for loop is working:

library(purrr)

f <- function(){
  tryCatch({
    stop("error")
  },
  error=function(cond) {
    message(cond)
  })
}

for(x in 1:10){
  f()
  print(x)
}
#> Error in doTryCatch(return(expr), name, parentenv, handler): error

Created on 2023-01-18 with reprex v2.0.2

I'd suggest going back to the drawing board and creating a reprex that actually illustrates the problem. I'm not seeing any evidence that this is related to purrr.