#> [1] FALSE
#> [1] TRUE
traceback() function.), } or " so the code AST cannot even be formed
And then of course there’s this classic:
Explanation: df is a closure, in this case, the function stats::df(), the density of F-distribution. (It is not a data frame, as you thought it was, ha!) It does not have elements that you could subset.
—Norm Matloff (emphasis added)
-Filipe Fortes
Being systematic often saves time in the end.
Concepts of unit tests are generally associated with R package development, see chapters on testing in “Writing R Packages”.
Automated testing sometimes brings a dilemma: you add something to a working function, and the stuff that you added does work according to the new tests, but an old test gets broken. This is may not be an error per se, but it shows a gap in your knowledge of and assumptions about the code (see Norm Matloff’s quote above) and you have to deal with that one way or another.
Parenthesis mismatches
[[...]] vs. [...]
== vs. =
Comparing real numbers exactly using == after calculations that result in non-integers
You expect a single value but your code gives you a vector
identical() or all(), or form the condition more thoroughlytraceback() to locate the error

traceback() shows the call stack that lead to the errorClick “Show traceback”:: 
Read bottom to top.
traceback() is confusing with lazy evaluation

traceback() limitsIn real world, traceback may look like 25 layers
dplyr, rlang, purrr and base and maybe packages you have never heard of, passing very opaque things to one another, your informatively named objects are abstracted somewhere in ..., .x and envir…rlang::global_handle() in .Rprofile makes traceback betterDebug > On Error > Break in Code to always jump to errorbrowser() to set a break point in codebrowser() can be conditionalA better practice would be to define your own debugging flags that would be turned off or disappear in production code
if (exists("my_debugging_flag")) browser()
browser() provides special commandsThese commands work in the Console; RStudio also makes the toolbar buttons available.
n): Execute the next steps): Dive into function (or n)f): Finish execution of the current loop/functionc): Continue regular execution of the function (leave interactive)Q): Stop debugging, terminate the function, and return to the global workspacebrowser() practical tipsCtrl+Enter to execute it
n “Next”browser()Activate:
Downsides:
options(error = recover) for interactive debugging promptTurn off with options(error = NULL)
debug(fn_name) to insert browser() in first line of fn_name()undebug(fn_name) to remove itdebugonce(fn_name) to do it once (similar to rerun with debug)utils::setBreakpoint("file_name", line_number)traceback(), browser() & where, and recover() are not consistenttraceback()recover()
callr::r() or fresh start to look for differencescallr::r(f, list(1, 2)) calls f(1, 2) in a fresh session
PATH environment variableR_LIBS environment variabledump.frames() is the equivalent to recover() for non-interactive code.Recall Chapter 8 on classed conditions:
rlang::abort(
message = "cli-formatted-message",
class = "your-special-class-to-distinguish-from-other-errors",
...
)
... so that error handlers and/or reporters can more effectively peek into your environment that erroredtestthat::expect_s3_class()rmarkdown::render("path/to/file.Rmd") instead of IDE knitting.
sink() for tricksy error handling
See also “Markdown test drive” in Jenny Bryan’s Happy Git With R book.
options(warn = 2) turns warnings into errors.traceback().“Object of type closure is not subsettable” (Jenny Bryan, rstudio::conf(2020))
“What They Forgot to Teach You About R” Chapter 12 (by Jenny Bryan and Jim Hester)
Minimal reprex for a shiny app (video by Hadley)
If you have access to “Code Complete”, it provides generic debugging principles that are meant to be applicable across programming languages