R Debugging
Debugging is the process of finding and fixing errors in your code. R provides a set of interactive tools that let you pause execution, inspect variables, step through code line by line, and trace exactly where things go wrong. Knowing these tools cuts debugging time from hours to minutes.
Types of Errors in R
Type Example How It Appears
──────────────────────────────────────────────────────────────────
Syntax missing ), extra { Error before code runs
Runtime divide by zero, wrong type Error during execution
Logic wrong formula, wrong index Code runs, wrong answer
Warning negative sqrt, NA coercion Runs but alerts you
Step 1: Read the Error Message
result <- mean(c(1, 2, "three")) # Warning: NAs introduced by coercion # [1] NA my_list <- list(a=1, b=2) my_list$c + 1 # Error in my_list$c + 1 : non-numeric argument # R tells you: # what went wrong (non-numeric argument) # where it went wrong (my_list$c + 1)
print() and cat() — Quick Inspection
compute_discount <- function(price, rate) {
cat("DEBUG price:", price, "rate:", rate, "\n") # print inputs
discount <- price * rate
cat("DEBUG discount:", discount, "\n") # print intermediate
final <- price - discount
return(final)
}
compute_discount(1000, 0.15)
# DEBUG price: 1000 rate: 0.15
# DEBUG discount: 150
# [1] 850
browser() — Interactive Breakpoint
analyze <- function(data) {
cleaned <- na.omit(data)
browser() # execution PAUSES here
result <- mean(cleaned)
return(result)
}
# When browser() pauses, you are in an interactive session:
# n → execute next line
# s → step INTO a function
# c → continue to next breakpoint
# Q → quit debugger
# ls() → see local variables
# print(cleaned) → inspect any variable
debug() — Step Through an Entire Function
my_func <- function(x, y) {
a <- x + y
b <- a * 2
return(b)
}
debug(my_func) # activate step-through
my_func(3, 4) # opens debugger — step through each line
undebug(my_func) # deactivate
debugonce() — Debug One Call Only
debugonce(my_func) # only debugs on the very next call my_func(5, 6) # debugger opens my_func(7, 8) # runs normally (debugger is off)
traceback() — Find Where an Error Occurred
f3 <- function() stop("Something broke")
f2 <- function() f3()
f1 <- function() f2()
f1() # Error in f3(): Something broke
traceback()
# 3: f3() at #1
# 2: f2() at #1
# 1: f1()
# traceback shows the call stack — read bottom-up to find root cause
tracebacks in RStudio
In RStudio, after an error, click the Show Traceback button that appears in the console. It shows the full call stack visually. Under Debug → On Error → Break in Code, RStudio drops you into the debugger automatically on every error.
options(error=recover) — Post-Mortem Debugging
options(error=recover) # activate f1() # when error occurs, shows call stack; type a number to enter that frame # Inside a frame: inspect local variables, test expressions # Type Q to exit options(error=NULL) # deactivate when done
Checking Inputs Early
# Validate function inputs at the top → errors are clear and early
safe_divide <- function(x, y) {
stopifnot(is.numeric(x), is.numeric(y)) # error if FALSE
if (y == 0) stop("Cannot divide by zero")
x / y
}
safe_divide(10, 0) # Error: Cannot divide by zero
safe_divide("a", 2) # Error: is.numeric(x) is not TRUE
Debugging Checklist
1. Read the error message in full 2. Run traceback() to find the source 3. Add print()/cat() around the suspect code 4. Use browser() to pause and inspect at the exact line 5. Check inputs — wrong type is the most common cause 6. Simplify — test with the smallest possible input 7. Check NA values — they propagate silently and cause logic errors
Debugging is a skill that improves with practice. The best R programmers are not those who write bug-free code on the first attempt — they are those who find and fix bugs quickly. Make browser() and traceback() part of your daily toolkit from day one.
