Debugging

2026-03-03

Debugging

Fixing problems

Main Tools

  • traceback() - the sequence of calls that led to the error
    • runs after you’ve gotten the error
  • Setting breakpoints
  • browser() - open an interactive session at an arbitrary breakpoint
    • can be used to create conditional breakpoints
  • debug() - open an interactive session where the error occurred
    • RStudio has a rerun with debug option

Or … low tech

  • print() statements
  • going line-by-line through the code interactively



Useful References: - Chapters Condition handling and Debugging by Hadley Wickham - RStudio Debugging Tools

First go to when you get an unexpected result

Placing output such as

  • cat()
  • str()
  • head()
  • summary()
  • print()

in strategic places in functions, are simple ways to build code up step-wise ensuring that each piece works (and figuring out where it fails)

Example: Read code

What does this function do? How does it work?

somefunction <- function (...) 
{
    k <- length(ll <- list(...))
    if (k == 0L) 
        return(invisible())
    mc <- match.call()
    for (i in 1L:k) if (!(is.logical(r <- ll[[i]]) && !any(is.na(r)) && 
        all(r))) {
        ch <- deparse(mc[[i + 1]], width.cutoff = 60L)
        if (length(ch) > 1L) 
            ch <- paste(ch[1L], "....")
        stop(paste(ch, " is not ", if (length(r) > 1L) 
            "all ", "TRUE", sep = ""), call. = FALSE)
    }
    invisible()
}

Step-by-step code checking

The browser() function allows stepping through your code.

help(browser)
  • n= executes the next line of code

  • s= step into next line. Same as n except if the line is a function. Will step into function

  • f = finish execution of this loop

  • c= continue execution until the end of the function, and exit

  • where gives you the stack information, the trace of the active function calls

  • Q exits browser()

Let’s try browser()

somefunction2() function has browser() in first line of function

somefunction2 <- function (...) 
{
    browser()
    k <- length(ll <- list(...))
    if (k == 0L) 
        return(invisible())
    mc <- match.call()
    for (i in 1L:k) if (!(is.logical(r <- ll[[i]]) && !any(is.na(r)) && 
        all(r))) {
        ch <- deparse(mc[[i + 1]], width.cutoff = 60L)
        if (length(ch) > 1L) 
            ch <- paste(ch[1L], "....")
        stop(paste(ch, " is not ", if (length(r) > 1L) 
            "all ", "TRUE", sep = ""), call. = FALSE)
    }
    invisible()
}

x <- 1
somefunction2(x==x, 1+1==2, c(7+5==11.99999, 1+1==2))

Browser enables:

  • printing of result of each line

  • checking changes to values

  • make sure that it results in what is expected

debug()

  • debug(f)
    automatically places a browser() statement in the first line of function f, i.e. browser starts every time the function f is being executed

  • undebug(f)
    removes it.

  • debugonce(f)
    Convenient alternative: starts the browser the next time function f is being executed

Setting error handling

  • options(error=recover) will drop user into browser() upon an error

  • options(error=NULL) default, do nothing

  • options(warn=2) sets maximum number of warnings to be 2

  • options(warn=0) warnings are stored until top level function finishes (default)

  • options(warn=-1) ignore all warnings

Your Turn: fix this!!

larger(x,y) is supposed to return the element-wise maximum of two vectors

larger <- function(x, y) { 
  y.is.bigger <- y > x 
    x[y.is.bigger] <- y[y.is.bigger] 
    x
} 
larger(c(1, 5, 10), c(2, 4, 11)) 
larger(c(1, 5, 10), 6)

why is there an NA in the second example? It should be a 6. Figure out why this happens, and try to fix it.