EVOLUTION-MANAGER
Edit File: topic-error-call.html
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>R: Including function calls in error messages</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" type="text/css" href="R.css" /> </head><body> <table width="100%" summary="page for topic-error-call {rlang}"><tr><td>topic-error-call {rlang}</td><td style="text-align: right;">R Documentation</td></tr></table> <h2>Including function calls in error messages</h2> <h3>Description</h3> <p>Starting with rlang 1.0, <code>abort()</code> includes the erroring function in the message by default: </p> <div class="sourceCode r"><pre>my_function <- function() { abort("Can't do that.") } my_function() #> Error in `my_function()`: #> ! Can't do that. </pre></div> <p>This works well when <code>abort()</code> is called directly within the failing function. However, when the <code>abort()</code> call is exported to another function (which we call an "error helper"), we need to be explicit about which function <code>abort()</code> is throwing an error for. </p> <h3>Passing the user context</h3> <p>There are two main kinds of error helpers: </p> <ul> <li><p> Simple <code>abort()</code> wrappers. These often aim at adding classes and attributes to an error condition in a structured way: </p> <div class="sourceCode r"><pre>stop_my_class <- function(message) { abort(message, class = "my_class") } </pre></div> </li> <li><p> Input checking functions. An input checker is typically passed an input and an argument name. It throws an error if the input doesn't conform to expectations: </p> <div class="sourceCode r"><pre>check_string <- function(x, arg = "x") { if (!is_string(x)) { cli::cli_abort("{.arg {arg}} must be a string.") } } </pre></div> </li></ul> <p>In both cases, the default error call is not very helpful to the end user because it reflects an internal function rather than a user function: </p> <div class="sourceCode r"><pre>my_function <- function(x) { check_string(x) stop_my_class("Unimplemented") } </pre></div> <div class="sourceCode r"><pre>my_function(NA) #> Error in `check_string()`: #> ! `x` must be a string. </pre></div> <div class="sourceCode r"><pre>my_function("foo") #> Error in `stop_my_class()`: #> ! Unimplemented </pre></div> <p>To fix this, let <code>abort()</code> knows about the function that it is throwing the error for by passing the corresponding function environment as <code>call</code> argument: </p> <div class="sourceCode r"><pre>stop_my_class <- function(message, call = caller_env()) { abort(message, class = "my_class", call = call) } check_string <- function(x, arg = "x", call = caller_env()) { if (!is_string(x)) { cli::cli_abort("{.arg {arg}} must be a string.", call = call) } } </pre></div> <div class="sourceCode r"><pre>my_function(NA) #> Error in `my_function()`: #> ! `x` must be a string. </pre></div> <div class="sourceCode r"><pre>my_function("foo") #> Error in `my_function()`: #> ! Unimplemented </pre></div> <h4>Input checkers and <code>caller_arg()</code></h4> <p>The <code>caller_arg()</code> helper is useful in input checkers which check an input on the behalf of another function. Instead of hard-coding <code>arg = "x"</code>, and forcing the callers to supply it if <code>"x"</code> is not the name of the argument being checked, use <code>caller_arg()</code>. </p> <div class="sourceCode r"><pre>check_string <- function(x, arg = caller_arg(x), call = caller_env()) { if (!is_string(x)) { cli::cli_abort("{.arg {arg}} must be a string.", call = call) } } </pre></div> <p>It is a combination of <code>substitute()</code> and <code>rlang::as_label()</code> which provides a more generally applicable default: </p> <div class="sourceCode r"><pre>my_function <- function(my_arg) { check_string(my_arg) } my_function(NA) #> Error in `my_function()`: #> ! `my_arg` must be a string. </pre></div> <h3>Side benefit: backtrace trimming</h3> <p>Another benefit of passing <code>caller_env()</code> as <code>call</code> is that it allows <code>abort()</code> to automatically hide the error helpers </p> <div class="sourceCode r"><pre>my_function <- function() { their_function() } their_function <- function() { error_helper1() } error_helper1 <- function(call = caller_env()) { error_helper2(call = call) } error_helper2 <- function(call = caller_env()) { if (use_call) { abort("Can't do this", call = call) } else { abort("Can't do this") } } </pre></div> <div class="sourceCode r"><pre>use_call <- FALSE their_function() #> Error in `error_helper2()`: #> ! Can't do this </pre></div> <div class="sourceCode r"><pre>rlang::last_error() #> <error/rlang_error> #> Error in `error_helper2()`: #> ! Can't do this #> --- #> Backtrace: #> 1. rlang (local) their_function() #> 2. rlang (local) error_helper1() #> 3. rlang (local) error_helper2(call = call) #> Run `rlang::last_trace()` to see the full context. </pre></div> <p>With the correct <code>call</code>, the backtrace is much simpler and let the user focus on the part of the stack that is relevant to them: </p> <div class="sourceCode r"><pre>use_call <- TRUE their_function() #> Error in `their_function()`: #> ! Can't do this </pre></div> <div class="sourceCode r"><pre>rlang::last_error() #> <error/rlang_error> #> Error in `their_function()`: #> ! Can't do this #> --- #> Backtrace: #> 1. rlang (local) their_function() #> Run `rlang::last_trace()` to see the full context. </pre></div> <h3>testthat workflow</h3> <p>Error snapshots are the main way of checking that the correct error call is included in an error message. However you'll need to opt into a new testthat display for warning and error snapshots. With the new display, these are printed by rlang, including the <code>call</code> field. This makes it easy to monitor the full appearance of warning and error messages as they are displayed to users. </p> <p>This display is not applied to all packages yet. With testthat 3.1.2, depend explicitly on rlang >= 1.0.0 to opt in. Starting from testthat 3.1.3, depending on rlang, no matter the version, is sufficient to opt in. In the future, the new display will be enabled for all packages. </p> <p>Once enabled, create error snapshots with: </p> <div class="sourceCode r"><pre>expect_snapshot(error = TRUE, { my_function() }) expect_snapshot_error(my_function()) </pre></div> <p>You'll have to make sure that the snapshot coverage for error messages is sufficient for your package. </p> <hr /><div style="text-align: center;">[Package <em>rlang</em> version 1.0.6 <a href="00Index.html">Index</a>]</div> </body></html>