withCallingHandlers() and Restarts
Use withCallingHandlers() for non-local condition handling.
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 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')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
- Errors, Warnings, and Messages in R
- tryCatch() for Error Recovery
- withCallingHandlers() and Restarts
- Writing Robust Functions with stop() and warning()