R Academy · Lesson

withCallingHandlers() and Restarts

Use withCallingHandlers() for non-local condition handling.

Lesson 3 of 413 steps

withCallingHandlers() and Restarts is a free R Academy lesson on CoddyKit. This is lesson 3 of 4. Any 3 lessons of this learning path are free to read in full — after that, CoddyKit PRO unlocks every lesson, plus hands-on practice with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, and your progress syncs across the web and the CoddyKit app. The R Academy course includes 4 lessons in total.

tryCatch vs withCallingHandlers

The key difference: tryCatch() establishes a non-local exit — once a condition is caught, control transfers out of the expression and does not return. withCallingHandlers() keeps the call stack intact and can resume execution after handling.

# tryCatch: execution does NOT continue after warning
tryCatch({
  warning('first')
  cat('This line is never reached\n')
}, warning = function(w) cat('tryCatch caught:', w$message, '\n'))

withCallingHandlers Keeps Execution

With withCallingHandlers(), the handler runs but execution continues after the condition is signaled (unless the handler itself throws an error or invokes a restart). The call stack is not unwound.

withCallingHandlers({
  warning('first warning')
  cat('Execution continues here\n')
  warning('second warning')
  cat('And here too\n')
}, warning = function(w) {
  cat('Handler saw:', w$message, '\n')
  invokeRestart('muffleWarning')
})

muffleWarning Restart

Calling invokeRestart('muffleWarning') inside a warning handler suppresses the warning from being printed by R's default handler. Without it, after your handler runs, R would still print the warning through its default machinery.

# Without muffleWarning: warning still shows after handler
withCallingHandlers({
  warning('test')
}, warning = function(w) {
  cat('Handler ran\n')
  # no muffleWarning: default handler still fires
})

muffleMessage Restart

Similarly, invokeRestart('muffleMessage') inside a message handler silences the message after your custom handler processes it. This is the mechanism behind suppressMessages().

withCallingHandlers({
  message('Progress: 50%')
  message('Progress: 100%')
}, message = function(m) {
  cat('[LOG]', trimws(m$message), '\n')
  invokeRestart('muffleMessage')
})

Logging Warnings Without Stopping

A practical use of withCallingHandlers(): collect warnings into a log vector while letting the code finish. tryCatch() would stop at the first warning; withCallingHandlers() collects all of them.

warnings_log <- character(0)
result <- withCallingHandlers({
  x <- log(-1)
  y <- sqrt(-4)
  c(x, y)
}, warning = function(w) {
  warnings_log <<- c(warnings_log, w$message)
  invokeRestart('muffleWarning')
})
cat('Warnings logged:', length(warnings_log), '\n')
cat(warnings_log, sep = '\n')

invokeRestart() Concept

Restarts are pre-defined recovery strategies that the code signaling the condition can offer. invokeRestart(name) selects one of those strategies. Built-in restarts include 'muffleWarning' and 'muffleMessage'; you can define custom ones with withRestarts().

# withRestarts() offers named recovery options
divide_safe <- function(x, y) {
  withRestarts(
    {
      if (y == 0) stop('division by zero')
      x / y
    },
    use_zero = function() 0,
    use_na   = function() NA
  )
}
# A handler can pick a restart
withCallingHandlers(
  divide_safe(10, 0),
  error = function(e) invokeRestart('use_zero')
)

Combining withCallingHandlers and tryCatch

You can nest the two: use withCallingHandlers() on the outside to log or count conditions, and tryCatch() on the inside to provide a fallback value. The outer handler sees the condition first.

n_warnings <- 0L
result <- withCallingHandlers(
  tryCatch(log(-1), warning = function(w) -999),
  warning = function(w) {
    n_warnings <<- n_warnings + 1L
    cat('Logged warning #', n_warnings, '\n')
  }
)
cat('Result:', result, '\n')

Custom Condition Classes

You can create custom condition classes by calling structure() on a list with class c('myError', 'error', 'condition'). Handlers match on class, so custom classes let you catch specific error types without catching all errors.

my_error <- function(msg, data = NULL) {
  structure(
    class = c('my_error', 'error', 'condition'),
    list(message = msg, data = data)
  )
}
tryCatch(
  stop(my_error('custom error', data = 42)),
  my_error = function(e) cat('Custom handler, data=', e$data, '\n'),
  error    = function(e) cat('Generic handler\n')
)

When to Use withCallingHandlers

Use withCallingHandlers() when you want to observe or log conditions without stopping execution. Use tryCatch() when you want to recover from a condition and return an alternative value. They solve different problems.

# withCallingHandlers: observe all, continue
withCallingHandlers(
  for (x in c(4, -1, 9, -4)) cat(suppressWarnings(sqrt(x)), ''),
  warning = function(w) cat('[warn]', '')
)
cat('\n')

The Condition Signaling Chain

When a condition is signaled, R walks up the call stack looking for handlers. withCallingHandlers() handlers run in the dynamic context (call stack intact). tryCatch() handlers run after unwinding — the original context is gone.

# Demonstrate: withCallingHandlers handler can see full stack
f <- function() {
  withCallingHandlers(
    g(),
    warning = function(w) {
      cat('Caught in f, stack length:', sys.nframe(), '\n')
      invokeRestart('muffleWarning')
    }
  )
}
g <- function() warning('from g')
f()

suppressWarnings() Implementation

suppressWarnings(expr) is essentially withCallingHandlers(expr, warning = function(w) invokeRestart('muffleWarning')). Understanding this lets you build your own variants that suppress only certain warning types.

# Suppress only NaN warnings, not others
suppress_nan_warn <- function(expr) {
  withCallingHandlers(expr, warning = function(w) {
    if (grepl('NaN', conditionMessage(w)))
      invokeRestart('muffleWarning')
  })
}
suppress_nan_warn(log(-1))   # NaN warning suppressed
tryCatch(
  suppress_nan_warn(warning('other issue')),
  warning = function(w) cat('Other warning kept:', w$message, '\n')
)

Quick Check

What is the main difference between withCallingHandlers() and tryCatch()?

Restarts: Key Takeaways

Key takeaways for withCallingHandlers and restarts:

  • withCallingHandlers() = local handler, call stack intact, execution can continue
  • tryCatch() = non-local exit, stack unwound, can return alternative value
  • invokeRestart('muffleWarning') suppresses the warning's default printing
  • invokeRestart('muffleMessage') suppresses message default printing
  • Use withCallingHandlers() to collect or log all conditions without stopping
  • Custom condition classes enable catching specific error types selectively
log_vec <- character(0)
withCallingHandlers({
  message('start')
  warning('low memory')
  message('end')
}, message = function(m) {
  log_vec <<- c(log_vec, paste('MSG:', trimws(m$message)))
  invokeRestart('muffleMessage')
}, warning = function(w) {
  log_vec <<- c(log_vec, paste('WARN:', w$message))
  invokeRestart('muffleWarning')
})
cat(log_vec, sep = '\n')
Free to start

Learn R with an AI tutor — free

Write and run real code in your browser, get instant help from a 24/7 AI tutor, and pick up where you left off on the web or in the app.

Courses
43
Lessons
159

Frequently Asked Questions

Is the “withCallingHandlers() and Restarts” lesson free?

Yes — any 3 lessons of the R Academy learning path, including “withCallingHandlers() and Restarts”, are free to read in full here on the web. After that, CoddyKit PRO unlocks every lesson, plus interactive practice with a built-in code editor and a 24/7 AI tutor. The R Academy course includes 4 lessons in total.

What will I learn in “withCallingHandlers() and Restarts”?

Use withCallingHandlers() for non-local condition handling. You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start R Academy?

No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners, so you can start here or from the beginning and move at your own pace. This is lesson 3 of 4.

How long does the “withCallingHandlers() and Restarts” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this R Academy lesson?

Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Errors, Warnings, and Messages in R
  2. tryCatch() for Error Recovery
  3. withCallingHandlers() and Restarts
  4. Writing Robust Functions with stop() and warning()
← Back to R Academy