Error-handling in Shiny observers seems to fall into two classes:
- some of the pre-built observers (that take, e.g. expressions converted into rendering functions) like
renderPrint()
that catch any errors or explicitstop()
conditions and re-throw them withreactiveStop()
for Shiny to handle without terminating the session, and - one's own observers that will terminate the Shiny session if an error bubbles-up that isn't caught and handled appropriately.
An example of case 1:
output$foo <- renderPrint(stop("foobar"))
This won't terminate the Shiny session, but instead catches the error and uses the default error-rendering function and class to draw "foobar" in the UI.
An example of case 2, with no termination:
observe(shiny:::reactiveStop("foobar"))
The Shiny session continues since the error has class shiny.silent.error
and Shiny's main event-loop knows how to deal with this.
An example of case 3, with termination:
observe(stop("foobar"))
The Shiny session terminates.
To prevent a termination, one could alter case 3 to:
observe({
tryCatch({
stop("foobar")
}, error = function(e) {
shiny:::reactiveStop(conditionMessage(e))
})
})
... or just handle the error any other way one wanted and prevent throwing another error.
So, if one wanted to always prevent Shiny sessions from terminating (save for some other error in the global scope that kills the R process), including error-handling (e.g. tryCatch()
or withCallingHandlers()
) in every observer would do the trick. But this seems cumbersome and I'm wondering if there's any common pattern to identify a generic handler that applies to all observers (that aren't already handling and swallowing errors) so the same error-handling code in Case 3b (above) doesn't have to be repeated frequently throughout the application.
The only idea I've come up with is something like re-defining observe
(and other reactive similars) to:
myObserve <- function(x, env = parent.frame(), quoted = FALSE, ...) {
f1 <- exprToFunction(x, env, quoted)
observe({
tryCatch({
f1()
}, error = function(e) {
shiny:::reactiveStop(e)
})
}, ...)
}
This feels awfully non-standard and I'm wondering if anyone else has wanted to (for lack of a better description) 'register' a standard error-handler for all observers in a Shiny app. (Both observers within a session and any global observers, which is an interesting use-case, too.)
(Maybe there's even a Shiny option for this that I'm just not aware of?)
Cheers and thanks for any thoughts!