Exception handling and stack unwinding in R
Asked Answered
F

1

5

In order to set up a coherent exception handling interface for my colleagues' and my R scripts, I would like to employ the following tryCatch structure.

  1. An outer tryCatch is wrapped around a given R script. It is used to catch and handle fatal errors that require the script to abort.
  2. User-specific tryCatch commands within the user's scripts. These should catch and, possibly, handle
    • 2a. non-fatal errors, where no script abortion is necessary
    • 2b. fatal-errors that require the script to abort. The error is handled by the outer tryCatch [see 1.]
    • 2c. fatal-errors with additional error information. Error handled by outer tryCatch.

The following code is how I would implement these features. However, since I am not an expert in R, I would like to ask whether this is a good approach. Specifically:

Q1. Is it ok not to specify an error handler in the inner tryCatch and to wait for the outer tryCatch to handle that error (see 2b. above and code below)?

Q2. Is rethrowing the same error (see 2c. above/below) within a handler correct/considered good coding style?

Thank you!

#outer tryCatch, see 1.
tryCatch({
  #user code block
  #2a. user specific tryCatch, object "vec" not defined
  tryCatch(print(vec),error=function(e) {print("Non-fatal error. Script execution continued.");print(e);})

  #2b. user specific tryCatch
  tryCatch(vec*2)

  #2c. user specific tryCatch
  tryCatch(vec*parameter1, error=function(e) {print("Additional fatal error information. Script execution aborted.");stop(e);})
  #end of user code block
},
     #outer tryCatch error handler in order to handle fatal errors
     error=function(e) {print("Fatal error");print(e);} 
    )
Fastigiate answered 1/10, 2012 at 11:25 Comment(0)
G
7

It's perfectly fine to catch only some errors, leaving other for the outer handler or no handler at all. The error system is quite a bit more flexible than typically used, so for re-throwing an error you might think of creating your own error type

ourError <-
    function(original, message, class="ourError")
{
    msg <- paste(message, conditionMessage(original), sep="\n  ")
    structure(list(message = msg, call = conditionCall(original)),
              class = c(class, class(original)))
}

and throwing and / or handling that

tryCatch(vec*parameter1, error=function(e) {
    err <- ourError(e, "addition fatal info; script aborted")
    stop(err)
})

One advantage of this is that additional behaviors can be specified in the top-level handler, using the class returned by ourError()

tryCatch({
    tryCatch(stop("oops"), error=function(e) {
        err <- ourError(e, "addition fatal info; script aborted",
                      c("fatal", "ourError"))
        stop(err)
    })
}, ourError=function(err) {
    message("We caught but didn't handle this:\n", err)
}, error =function(err) {
    message("This one got away: ", err)
})
Glyceric answered 1/10, 2012 at 15:51 Comment(1)
Thank you very much for your detailed answer and for pointing at the possibility of attaching additional information to the (derived) error object itself. (Unfortunately I do not have enough reputation to upvote your answer...)Fastigiate

© 2022 - 2024 — McMap. All rights reserved.