R Debugging Using trace() and recover()

#===========================================
# EXAMPLE 1: Simple Function
#===========================================

cube_num <- function(n) {
  
  result <- n^3
  
  return(result)
}

cube_num(4)
## [1] 64

Expected Output:

[1] 64


#===========================================
# EXAMPLE 2: Function with Condition
#===========================================

check_division <- function(x, y) {
  
  if (y == 0) {
    stop("Division by zero is not allowed")
  }
  
  answer <- x / y
  
  if (answer > 0) {
    cat("Result is Positive:", answer, "\n")
  } else {
    cat("Result is Negative:", answer, "\n")
  }
}

check_division(12, 3)
## Result is Positive: 4

Expected Output:

Result is Positive: 4


Function for Debugging

#===========================================
# EXAMPLE 3: Intentional Error
#===========================================

check_division_error <- function(x, y) {
  
  if (y == 0) {
    stop("Division by zero is not allowed")
  }
  
  answer <- x / y
  
  if (answer > 0) {
    cat("Result is Positive:", answer, "\n")
  } else {
    cat("Result is Negative:", answer, "\n")
  }
}

check_division_error(10, 0)
## Error in `check_division_error()`:
## ! Division by zero is not allowed

Expected Output:

Error in check_division_error(10, 0) :

Division by zero is not allowed


1. Using traceback()

This shows the sequence of function calls after an error.

traceback()
## No traceback available

Expected Output:

Shows call stack in console


2. Using debug()

This lets you run line by line.

debug(check_division)

check_division(8, 2)
## debugging in: check_division(8, 2)
## debug: {
##     if (y == 0) {
##         stop("Division by zero is not allowed")
##     }
##     answer <- x/y
##     if (answer > 0) {
##         cat("Result is Positive:", answer, "\n")
##     }
##     else {
##         cat("Result is Negative:", answer, "\n")
##     }
## }
## debug: if (y == 0) {
##     stop("Division by zero is not allowed")
## }
## debug: answer <- x/y
## debug: if (answer > 0) {
##     cat("Result is Positive:", answer, "\n")
## } else {
##     cat("Result is Negative:", answer, "\n")
## }
## debug: cat("Result is Positive:", answer, "\n")
## Result is Positive: 4 
## exiting from: check_division(8, 2)

Expected Output:

Browser mode starts

Browse[1]>

Use: - n = next line - c = continue - Q = quit


3. Using trace()

This adds temporary tracing code inside a function.

trace(
  "check_division",
  tracer = quote(cat("Tracing function: x =", x, " y =", y, "\n")),
  print = FALSE
)
## [1] "check_division"
check_division(20, 5)
## Tracing function: x = 20  y = 5 
## Result is Positive: 4

Expected Output:

Tracing function: x = 20 y = 5

Result is Positive: 4

Remove trace:

untrace("check_division")

4. Using recover()

This helps inspect errors interactively.

options(error = recover)

nested_func1 <- function(a) {
  nested_func2(a)
}

nested_func2 <- function(a) {
  sqrt("Rwanda")   # intentional error
}

nested_func1(5)
## Error in sqrt("Rwanda"): non-numeric argument to mathematical function

Expected Output in Console:

Error in sqrt(“Rwanda”) :

non-numeric argument to mathematical function

Enter a frame number, or 0 to exit

1: nested_func1(5)

2: nested_func2(a)

Then type:

2

You will enter:

Browse[1]>

Useful commands: - ls() → list variables - where → show stack - Q → quit - c → continue


Summary

Function Purpose
traceback() Shows call stack after error
debug() Runs function line by line
trace() Monitors function execution
recover() Enters error inspection mode