withCallingHandlers() and Restarts
Use withCallingHandlers() for non-local condition handling.
withCallingHandlers() and Restarts is a free R Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
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 continuetryCatch()= non-local exit, stack unwound, can return alternative valueinvokeRestart('muffleWarning')suppresses the warning's default printinginvokeRestart('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')Frequently asked questions
Is the “withCallingHandlers() and Restarts” lesson free?
Yes — the full text of “withCallingHandlers() and Restarts” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.
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; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
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
- Errors, Warnings, and Messages in R
- tryCatch() for Error Recovery
- withCallingHandlers() and Restarts
- Writing Robust Functions with stop() and warning()