shiny icon indicating copy to clipboard operation
shiny copied to clipboard

Consider using rlang::abort()

Open hadley opened this issue 6 years ago • 9 comments

If you use rlang::abort() instead of base::stop(), you'll be able to get much nicer call traces on error.

hadley avatar May 31 '19 12:05 hadley

I'm poking at this. Here's example behavior for one stop() instance:

Screen Shot 2019-06-25 at 9 37 52 AM

@hadley, are there better motivating use cases here that would highlight the improved functionality? Or do you only realize the benefits of abort if you intelligently use the .subclass and ... available in abort?

trestletech avatar Jun 25 '19 14:06 trestletech

In case either of you are not aware, we do quite extensive work to try to get helpful stack traces in Shiny already: https://github.com/rstudio/shiny/wiki/Stack-traces-in-R

jcheng5 avatar Jun 25 '19 21:06 jcheng5

I forget exactly what you need to do, but you can get the full tree like this (reprex did it for me):

f <- function() g()
g <- function() try(h())
h <- function() rlang::abort("!")

f()
#> Error : !
#> Backtrace:
#>     █
#>  1. └─global::f()
#>  2.   └─global::g()
#>  3.     ├─base::try(h())
#>  4.     │ └─base::tryCatch(...)
#>  5.     │   └─base:::tryCatchList(expr, classes, parentenv, handlers)
#>  6.     │     └─base:::tryCatchOne(expr, names, parentenv, handlers[[1L]])
#>  7.     │       └─base:::doTryCatch(return(expr), name, parentenv, handler)
#>  8.     └─global::h()

Or you can request the simplification to the terminal branches:

print(rlang::last_trace(),  simplify = "branch")
#>  1. global::f()
#>  2. global::g()
#>  8. global::h()

@jcheng5 I think we now have the tooling in rlang to supplant a lot of that custom work

hadley avatar Jun 25 '19 22:06 hadley

(Oh, we don't do anything for errors that make it all the way to the top level, only for stack traces that are caught and printed without exiting Shiny.)

jcheng5 avatar Jun 27 '19 18:06 jcheng5

:+1: on this, it would be a great help for debugging deeply nested modular apps to be able to locate errors in functions that don't already use rlang::abort style error handling, such as any functions from the packages that come bundled with base (utils, stats etc). I currently resort to using options(shiny.error = utils::recover) to locate the observer/reactive where the error occurred and intuit the source of the error from there, but that's not very precise. An rlang::trace_back for all the errors that occur in a shiny app would be amazing!

yogat3ch avatar Jan 24 '23 21:01 yogat3ch

@yogat3ch Are you not already seeing complete stack traces, including source file names, line numbers, and reactive/observer names being emitted to the console? Can you show an example of the kind of stack traces you get when a non-rlang error is thrown?

jcheng5 avatar Jan 25 '23 00:01 jcheng5

@jcheng5 Most of the time shiny gives errors with extended stack traces and line numbers. On occasion, I'll get just an error. I'll provide a screenshot and context next time that occurs.

yogat3ch avatar Jan 25 '23 10:01 yogat3ch

Hi @jcheng5, It took a while for one to surface organically - here's an example that demonstrates how a base::switch call nested deeply in a modularized shiny app doesn't provide any trace back context: image

yogat3ch avatar Jan 30 '23 22:01 yogat3ch

Is there a way to make the links clickable? image

olivroy avatar Oct 03 '24 20:10 olivroy