Condition handlers are functions established on the evaluation stack (see ctxt_stack()) that are called by R when a condition is signalled (see cnd_signal() and abort() for two common signal functions). They come in two types: exiting handlers, which jump out of the signalling context and are transferred to with_handlers() before being executed. And inplace handlers, which are executed within the signal functions.

with_handlers(.expr, ...)



An expression to execute in a context where new handlers are established. The underscored version takes a quoted expression or a quoted formula.


Named handlers. Handlers should inherit from exiting or inplace. See exiting() and inplace() for constructing such handlers. Dots are evaluated with explicit splicing.


An exiting handler is taking charge of the condition. No other handler on the stack gets a chance to handle the condition. The handler is executed and with_handlers() returns the return value of that handler. On the other hand, in place handlers do not necessarily take charge. If they return normally, they decline to handle the condition, and R looks for other handlers established on the evaluation stack. Only by jumping to an earlier call frame can an inplace handler take charge of the condition and stop the signalling process. Sometimes, a muffling restart has been established for the purpose of jumping out of the signalling function but not out of the context where the condition was signalled, which allows execution to resume normally. See rst_muffle() the muffle argument of inplace() and the mufflable argument of cnd_signal().

Exiting handlers are established first by with_handlers(), and in place handlers are installed in second place. The latter handlers thus take precedence over the former.

See also

exiting(), inplace().


# Signal a condition with cnd_signal(): fn <- function() { g() cat("called?\n") "fn() return value" } g <- function() { h() cat("called?\n") } h <- function() { cnd_signal("foo") cat("called?\n") } # Exiting handlers jump to with_handlers() before being # executed. Their return value is handed over: handler <- function(c) "handler return value" with_handlers(fn(), foo = exiting(handler))
#> [1] "handler return value"
# In place handlers are called in turn and their return value is # ignored. Returning just means they are declining to take charge of # the condition. However, they can produce side-effects such as # displaying a message: some_handler <- function(c) cat("some handler!\n") other_handler <- function(c) cat("other handler!\n") with_handlers(fn(), foo = inplace(some_handler), foo = inplace(other_handler))
#> some handler! #> other handler! #> called? #> called? #> called?
#> [1] "fn() return value"
# If an in place handler jumps to an earlier context, it takes # charge of the condition and no other handler gets a chance to # deal with it. The canonical way of transferring control is by # jumping to a restart. See with_restarts() and restarting() # documentation for more on this: exiting_handler <- function(c) rst_jump("rst_foo") fn2 <- function() { with_restarts(g(), rst_foo = function() "restart value") } with_handlers(fn2(), foo = inplace(exiting_handler), foo = inplace(other_handler))
#> [1] "restart value"