[1] FALSE
28 Sep 2026
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License (CC BY-SA4.0).
These materials have been developed in part by Hanne Oberman, Shannon Pileggi, Jon Harmon, and Danielle Navarro.
When using these materials, you must ensure that the content is not used for generative AI model training.
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.
traceback() function.The best way to deal with errors is not to have any at all
Popular IDEs for R:
Some file names are good ✅
Other file names are bad ❌
a and A the same)ñ (but consider accessibility!)- to mean “different words that are part of the same chunk”_ to separate different chunksidentifier] [author] [title] [section (optional)]Love the slug 🐌
Concise, meaningful description
Usually appended to the end
File names sometimes need to include dates
… but these dates don’t sort in chronological order
Happily, these dates do!
Use numbers as a prefix to order files
Left pad with 0 so that all numbers have same length
Numbers are more robust than letters (alphabetical order varies)
.Rproj) files to organize your files
setwd()
.Rproj filehere::here()
here("data", "file_i_want.csv")
Three files inside one folder
The “file paths” are written like this
project/01_initial-plan.docx project/02_data-collection.R project/03_initial-analysis.R
Three files inside one folder
Except on Windows…
project\01_initial-plan.docx project\02_data-collection.R project\03_initial-analysis.R
project/overview.docxproject/data/data_ex01.csvproject/data/data_ex02.csvproject/analysis/ex01_01_descriptive-statistics.Rproject/analysis/ex01_02_inferential-statistics.Rproject/analysis/ex02_01_descriptive-statistics.Rproject/analysis/ex02_02_inferential-statistics.Rproject/report/2021-03-11_report_getting-started.docxDebugging is like being the detective in a crime movie where you’re also the murderer.
-Filipe Fortes
), } or " so the code AST cannot even be formed
Parenthesis mismatches
[[...]] vs. [...]
== vs. =
Comparing real numbers exactly using == after calculations that result in non-integers
Type coercion and dropping dimensions
\(\rightarrow\) you may need identical() or all(), or form the condition more thoroughly
SEARCH
Teach Me How To Google, Samantha Csik (2022)
slides, recording
RESET
Restart R, especially when things get weird.
REPREX
make a reprex….. please, Sharla Gelfand (2021)
slides, recording, https://reprex.tidyverse.org/
AI (optional)
Claude, ChatGPT, etc.
??, or usethis::browse_package())usethis::browse_github(), or StackOverflow)Have you tried turning it OFF and ON again?
Session -> Restart R or
Ctrl + Shift + F10 (Windows),
Cmd + Shift + 0 / Cmd + Shift + F10 (Mac)
Which persist after rm(list = ls())?
| Option | Persists? |
|---|---|
A. library(dplyr)
|
|
B. summary <- head
|
|
C. options(stringsAsFactors = FALSE)
|
|
D. Sys.setenv(LANGUAGE = "fr")
|
|
E. x <- 1:5
|
|
F. attach(iris)
|
Which persist after rm(list = ls())?
| Option | Persists? |
|---|---|
A. library(dplyr)
|
|
B. summary <- head
|
|
C. options(stringsAsFactors = FALSE)
|
|
D. Sys.setenv(LANGUAGE = "fr")
|
|
E. x <- 1:5
|
|
F. attach(iris)
|
rm(list = ls())stringsAsFactors
Set your Workspace options as shown.
Tools -> Global Options -> Workspace
reprex() functionreprex() records:
reprex
Created on 2026-09-24 with reprex v2.1.1
Being systematic often saves time in the end.
Finding your bug is a process of confirming the many things that you believe are true — until you find one which is not true.
—Norm Matloff (emphasis added)
R installed? Pretty recent?
\(\rightarrow\) Recommended R ≥ 4.4.0
RStudio installed?
\(\rightarrow\) I’m on 2026.08.1+195
Optional:
Ready to build packages?
\(\rightarrow\) devtools::has_devel()
\(\rightarrow\) Your system is ready to build packages!
⚠️ name your script with functions
⚠️ source your script with functions
for the best debugging experience 😎
traceback
\(\rightarrow\) location where did the error occur
interactive debugger
\(\rightarrow\) context why did the error occur
your code vs their code
This is a lot.
These tools achieve similar objectives slightly differently.
People don’t generally use all of these tools at once. They pick and choose the ones they like.
Use traceback() to locate the error
shows the sequence of calls that lead to the error.
the trace back is also known as:
call stack, stack trace, & back trace
options(error = rlang::entrace)
could go in your .Rprofile
traceback() shows the call stack that lead to the errorClick “Show traceback”::
Read bottom to top.
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…browser() opens the interactive debugger.
Modify the function by inserting a browser() statement.
Source the function.
Execute the function.
Use browser() to set a break point in code
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”Download the exercises
Complete 01_exercise to practice debugging your own code.
01_debugging_spartan.R (directions to explore without suggested code)
01_debugging_comfy.R (directions to explore with suggested code)
01_debugging_solution.R (directions to explore with code solutions)
Remember to restart R as indicated:
Session -> Restart R (in RStudio IDE), or
[ctrl]/[cmd] + [shift] + [F10]
Debug > On Error > Break in Code to always jump to errorbrowser()
red circle = breakpoint = browser()
(but you don’t have to change your code)
Set / re-set an editor breakpoint:
click to the left of the line number in the source file
press Shift+F9 with your cursor on the line
red circle = breakpoint = browser()
(but you don’t have to change your code)
To activate, either
click IDE Source button, or
debugSource(“demo/my_functions.R”)
RStudio IDE cheatsheet
# IDE Error Inspector not triggered
f <- function(x) x + 1
g <- function(x) f(x)
g("a")
# IDE Error Inspector not triggered
strsplit(factor("a,b"), ",")
# IDE Error Inspector not triggered
f <- function(x) strsplit(x, ",")
f(factor("a,b"))
# IDE Error Inspector yes triggered
g <- function(x) f(x)
g(factor("a,b"))
The error inspector is only invoked if your code is involved.
To have RStudio invoke the debugger on any error,
Tools -> Global Options -> General -> Advanced -> Debugging
uncheck Use debug error handler only when my code contains errors.
Complete 02_exercise to practice debugging a slightly different version of your own code, using features from the RStudio IDE.
debug() = browser()/ breakpoint in first line of function
interactive debugger is initiated every time g() is executed, until undebug("g")
depending on function internals, this can… trap you in the debugger 😬
debugonce() = browser()/ breakpoint in first line of function
one time only!
interactive debugger initiated a single time when g() is executed
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) we already discussed options(error = rlang::entrace)
for a richer traceback on error
🤠 options(error = recover)
displays an interactive prompt with frames
you select the frame to enter the debugger
options(error = recover) for interactive debugging promptTurn off with options(error = NULL)
trace(what = fun, tracer = browser)
is equivalent to
inserting browser() in first line of function
debug(fun)
you an also insert any code at any location in function
trace(what = fun, tracer = browser, at = 2) browser() at second step of fun
untrace(fun) cancels the tracing
trace(what = colSums, tracer = browser)
is equivalent to
inserting browser() in first line of colSums
if we had the source code
debug(colSums)
investigate 🧐 the function with as.list() + body()
Complete 03_exercise to practice debugging others’ code.
rmarkdown chunk option error = TRUE enables knitting with errors
insert knitr::knit_exit() and interactively work through .Rmd
Adv R Ch 22.5.3 RMarkdown https://adv-r.hadley.nz/debugging.html#rmarkdown
WTF Ch 11.4 Debugging in Rmarkdown documents https://rstats.wtf/debugging-r-code.html#debugging-in-r-markdown-documents
tracebacks can be verbose with pipes
gives trimmed tracebacks when using pipes
Matt Dray 2019 blog post Fix leaky pipes in R
If you want to dig deeper into a warning, you can convert them to errors to initiate debugging tools.
?options
options(warn = 0) # default, stores warnings until top-level function returns
options(warn = 1) # warnings are printed as they occur
options(warn = 2) # upgrades warnings to errors
# initiate recover on warning
options(warn = 2, error = recover)
# restore original settings
options(warn = 0, error = NULL)Functions can fail without errors
options(warn = 2) turns warnings into errors.traceback().traceback(), browser() & where, and recover() are not consistenttraceback() and rlang functions.traceback()
recover()
traceback
interactive debugger
your code vs
their code
Some useful resources on debugging:
“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 Wickham)
If you have access to “Code Complete”, it provides generic debugging principles that are meant to be applicable across programming languages
WTF Ch 11 Debugging R code
https://rstats.wtf/debugging-r-code.html
Advanced R Ch 22 Debugging
https://adv-r.hadley.nz/debugging.html
Jenny Bryan 2020 RStudio Conf Keynote Object of type closure is not subsettable
https://github.com/jennybc/debugging#readme
Amanda Gadrow 2018 Webinar Debugging techniques in RStudio
https://www.rstudio.com/resources/webinars/debugging-techniques-in-rstudio/
Jim Hester 2019 Introduction to debugging in R and RStudio
https://www.jimhester.com/talk/2019-crug-debugging/
Maëlle Salmon 2021 How to become a better R code detective?
https://masalmon.eu/2021/07/13/code-detective/
Kara Woo 2019 RStudio Conf Box plots A case study in debugging and perseverance https://www.rstudio.com/resources/rstudioconf-2019/box-plots-a-case-study-in-debugging-and-perseverance/
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.traceback() is confusing with lazy evaluationbrowser() 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()
Debugging tools in Positron are still under active development. browser() and debugonce() are supported, but breakpoints aren’t.
Positron facilitates debugging C++ code posit::conf(2024), Davis Vaughn, Aug 2024
Positron IDE Debugger for R Demo with Nested Functions (Public Beta) YouTube, James Balamuta, Jun 2024
Positron: current joys and pains, blog post, Athanasia Mowinckel, Apr 2025
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.
See Advanced R book (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()
lintr can automate checks to warn you if you leave browser() statements in your code.
https://lintr.r-lib.org/reference/default_undesirable_functions.html
usethis::use_github_action("lint")
Live example in devtools https://github.com/r-lib/devtools/actions/workflows/lint.yaml
rmarkdown::render("path/to/file.Rmd") instead of IDE knitting.
sink() for tricksy error handlingSee also “Markdown test drive” in Jenny Bryan’s Happy Git With R book.