knitr icon indicating copy to clipboard operation
knitr copied to clipboard

feat: provide a stack trace for fatal errors in chunk

Open eternal-flame-AD opened this issue 1 year ago • 4 comments

This is the idea which will provide a backtrace of the error instead of just the message: https://github.com/r-lib/evaluate/pull/226#issuecomment-2403428797.

Before:

Error in select(., nothing) : Can't select columns that don't exist.
✖ Column `nothing` doesn't exist.

Quitting from lines 3-11 [unnamed-chunk-1] (bad.Rmd)

After:

Error in `eng_r()`:
! Error in chunk unnamed-chunk-1
Caused by error in `select()`:
! Can't select columns that don't exist.
✖ Column `nothing` doesn't exist.
Backtrace:
 1. global myerror()
 4. dplyr:::select.data.frame(., nothing)

Quitting from lines 3-11 [unnamed-chunk-1] (bad.Rmd)

eternal-flame-AD avatar Oct 10 '24 14:10 eternal-flame-AD

CLA assistant check
All committers have signed the CLA.

CLAassistant avatar Oct 10 '24 14:10 CLAassistant

I will think about this more later today, but my initial thoughts for this part is:

Rlang documentation says

entrace() is meant to be used as a global handler.

It seems to be a UX improvement tool to make stop() emit a trace rather than a wrapper that magically adds stack trace. I tried a toy example:

myerr <- \() { entrace(cnd = errorCondition("Test")) } # no stack trace when called
trace <- \() myerr() # stack trace only contains trace(), not myerr()

xfun::handle_error seems to be undocumented API and, is this meant to catch errors that are even supposed to crash R so it is written as an exit handler instead of just a catch?

eternal-flame-AD avatar Oct 10 '24 15:10 eternal-flame-AD

xfun:::handle_error() doesn't try to catch errors at all (it doesn't use tryCatch() or withCallingHandlers()). It is intended to run a "handler" function when errors occur in the expression. You can basically treat xfun:::handle_error(expr) as expr.

yihui avatar Oct 10 '24 15:10 yihui

My original thought about writing it there was I think an R backtrace is only useful for R engine. Seems only R engine output is improved (presumably because the call to evaluate() here erased the call stack before your chunk-level handler took it.

We probably don't really have a case of where we intentionally want to suppress traces for one type of engine. It is fine and probably clearer to lift it up.

Found another issue when testing this that I can reproduce on master ... I will look at it this weekend to see what condition triggered this and if it is my environment is bad or another bug


```{python}
print("Hello Python!")
```

```
  Error in loadable(pkg) : node stack overflow
Error during wrapup: node stack overflow
Error: no more error handlers available (recursive errors?); invoking 'abort' restart

Quitting from lines 90-91 [hello] (error.Rmd)
```
```

eternal-flame-AD avatar Oct 11 '24 03:10 eternal-flame-AD

Just a bump in case this got lost :)

eternal-flame-AD avatar Oct 22 '24 23:10 eternal-flame-AD

No it does not get lost. I have a current workload on Quarto work because we're are working on v1.6 release shortly. So my focus is not right now in R work and I prefer to be focused on topic when I worked on them - sorry to keep you waiting. I'll come back to this very quickly !

Thank you for your patience

cderv avatar Oct 23 '24 09:10 cderv

FWIW I am currently looking into this. However, I am looking into this at evaluate level to auto entrace error when rlang available. There is possibly some change to make in knitr, evaluate and rlang to make stuff work, espacially with recent evaluate change.

cderv avatar Dec 16 '24 14:12 cderv

So change in evaluate have been released to CRAN. This is now what we get when rlang is available

---
title: test
---

```{r}
library(tidyverse)

mtcars %>%
  ggplot(aes(x = wt, y = mpg)) +
  geom_qq() +
  geom_smooth(method = "lm")
```

> knitr::opts_chunk$set(error = FALSE)
> knitr::knit("test.Rmd")


processing file: test.Rmd
  |...................................................................................................................................................| 100% [unnamed-chunk-1]

Error in `geom_qq()`:
! Problem while computing stat.
ℹ Error occurred in the 1st layer.
Caused by error in `compute_layer()`:
! `stat_qq()` requires the following missing aesthetics: sample.
Backtrace:
  1. knitr::knit("test.Rmd")
  2. knitr:::process_file(text, output)
       at knitr/R/output.R:253:3
  5. knitr:::process_group(group)
       at knitr/R/output.R:311:5
     ...
 44. ggplot2 (local) f(l = layers[[i]], d = data[[i]])
 45. l$compute_statistic(d, layout)
 46. ggplot2 (local) compute_statistic(..., self = self)
 47. self$stat$compute_layer(data, self$computed_stat_params, layout)
 48. ggplot2 (local) compute_layer(..., self = self)

Quitting from lines 5-12 [unnamed-chunk-1] (test.Rmd)

I was thinking a more reduced backtrace, but somehow with simpler document this is better

---
title: document with error
---

```{r}
f <- function() g()
g <- function() h()
h <- function() rlang::abort("!")
f()
```
> knitr::knit("test.Rmd")


processing file: test.Rmd
  |...................................................................................................................................................| 100% [unnamed-chunk-1]

Error in `h()`:
! !
Backtrace:
 1. global f()
 2. global g()
 3. global h()

Quitting from lines 5-10 [unnamed-chunk-1] (test.Rmd)

So I don't think this PR is needed anymore. I'll keep trying improving but I need more rlang knowledge

cderv avatar Jan 14 '25 16:01 cderv