33

I observed a different between an interactive and non-interaction R session about traceback() which I do not understand. For the code below, it will produce an error, but in an interactive R session, I can see the traceback information, whereas if I save the code to test.R and call it via Rscript test.R or R -f test.R, I can no longer see the traceback:

f = function() {
  on.exit(traceback())
  1 + 'a'
}
f()

In an interactive R session:

> f = function() {
+   on.exit(traceback())
+   1 + 'a'
+ }
> f()
Error in 1 + "a" : non-numeric argument to binary operator
1: f()

Non-interactive execution:

$ Rscript test.R 
Error in 1 + "a" : non-numeric argument to binary operator
Calls: f
No traceback available 
Execution halted

I did not see an explanation in ?traceback, and I'm wondering if there is a way to enable traceback for non-interactive R sessions. Thanks!

Yihui Xie
  • 28,913
  • 23
  • 193
  • 419
  • `traceback` looks for an object called `.Traceback` in the `baseenv()`. It looks (from `src/main/errors.c`) like this is only created if, among other conditions, `R_Interactive || haveHandler`. Without `.Traceback`, you will get the message "No traceback available". There's also a warning under `?traceback` that mentions `.Traceback`. – BenBarnes Oct 29 '12 at 08:40
  • If you just set options(error=traceback) in your script and remove your on.exit call, you will have the desired effect. Although it does create duplication because of the "step" of the error. – Brandon Bertelsen Oct 29 '12 at 08:44
  • @BrandonBertelsen, As far as I can tell, that still won't give you access to traceback info, although the call stack gets returned anyway in the case of an error in a non-interactive session. – BenBarnes Oct 29 '12 at 08:53
  • http://r.789695.n4.nabble.com/Automatic-traceback-td845199.html – Brandon Bertelsen Oct 29 '12 at 08:58
  • @BenBarnes Your comments are a useful Answer, care t add them as such? – Gavin Simpson Oct 29 '12 at 09:13

4 Answers4

36

With default values of its arguments, traceback() will look for an object named .Traceback in the baseenv() for information on the call stack. It looks (from src/main/errors.c) like .Traceback is only created if, among other conditions, R_Interactive || haveHandler, suggesting that this object is not created during non-interactive sessions. If there is no object named .Traceback, you will get the message "No traceback available".

However, by passing a non-NULL value to the x argument of traceback(), one can obtain information about the call stack from a non-interactive session. With a non-zero integer value (indicating the number of calls to skip in the stack), c-level functions (R_GetTraceback) are called to investigate the call stack instead of looking in .Traceback.

So there are a couple ways to obtain traceback information in a non-interactive session:

f = function() {
  on.exit(traceback(1))
  1 + 'a'
}
f()

Or, setting options as Brandon Bertelsen suggested

options(error=function()traceback(2))

The different values passed to x in the two examples account for the different number of functions to skip

  1. In the on.exit example, traceback(1) skips the call to traceback().

  2. In the example setting options, there is an extra anonymous function that calls traceback() which should/could also be skipped.

In the example in the OP, there's not much more information gained by using traceback() compared to the automatic traceback provided in the case of an error in a non-interactive session. However, with functions that take (and are passed) arguments, using traceback() will be much more informative than the standard presentation of the call stack in the non-interactive session.

BenBarnes
  • 19,114
  • 6
  • 56
  • 74
  • Thanks a lot! I did not realize that setting `x` to a number also works for non-interactive R sessions. – Yihui Xie Oct 29 '12 at 17:02
  • 9
    Note that using `options(error=function() traceback(2))` will cause scripts to exit successfully even when an error occurs! A solution is to use `error=function() { traceback(2); if(!interactive()) quit("no", status = 1, runLast = FALSE) }` instead (the stop arguments used here are from the [default error behaviour](https://stat.ethz.ch/R-manual/R-devel/library/base/html/stop.html)). – dshepherd Apr 06 '15 at 11:37
  • This is still somewhat problematic. When using the `options(error=function()traceback(2))` leads to anomalous output. See https://stackoverflow.com/questions/52933667/r-executes-code-in-function-when-there-is-a-syntax-error-and-function-is-not-exp?noredirect=1#comment92778349_52933667 – irritable_phd_syndrome Oct 23 '18 at 11:49
5

There is also the possibility to dump debugging information and load them later on. (See good ?debugger help pages and comments on the topic)

via e.g.:

options(error = quote(dump.frames("testdump", TRUE)))

...

load("testdump.rda")
debugger(testdump)

or

options(error = quote({dump.frames(to.file = TRUE); q(status = 1)}))
petermeissner
  • 12,234
  • 5
  • 63
  • 63
2

The problem with options(error=function()traceback(2)) is that it does not stop the script execution, which is really really dangerous.

To avoid that you can use:

options(error = function() { 
  traceback(2)
  options(error = NULL)
  stop("exiting after script error") 
})

which will properly print the stack trace, then exit properly.

Karl Forner
  • 4,175
  • 25
  • 32
  • Does this approach produce an exit code that indicates that the program failed? – carbocation Dec 07 '22 at 05:38
  • 1
    yes. since it exits via a stop() – Karl Forner Dec 07 '22 at 09:15
  • Thanks. Digging into [the docs](https://stat.ethz.ch/R-manual/R-devel/library/base/html/stop.html) confirms: `The default behaviour (the NULL error-handler) in interactive use is to return to the top level prompt or the top level browser, and in non-interactive use to (effectively) call q("no", status = 1, runLast = FALSE).` – carbocation Dec 07 '22 at 17:52
1

BenBarnes's answer and dshepherd's comment are life saving. I will add one more parameter to avoid clogging up the screen in case one of the parameters is very big.

options(error=function(){traceback(2,max.lines=3);if(!interactive())quit("no",status=1,runLast=FALSE)})
Valentas
  • 2,014
  • 20
  • 24