purrr::safely() / purrr::possibly() / purrr::quietly()
safely(.f, otherwise = NULL, quiet = TRUE)
possibly(.f, otherwise = NULL, quiet = TRUE)
quietly(.f) list · Updated March 13, 2026 · Tidyverse The safely(), possibly(), and quietly() functions from purrr wrap existing functions to handle errors, warnings, and messages gracefully. These wrappers are essential when applying functions across many elements—such as rows of a data frame or elements of a list—where a single failure would abort the entire operation.
Syntax
library(purrr)
# safely: captures both errors and results
safely(.f, otherwise = NULL, quiet = TRUE)
# possibly: returns a default value on error
possibly(.f, otherwise = NULL, quiet = TRUE)
# quietly: captures warnings and messages but lets errors fail
quietly(.f)
Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
.f | function | — | The function to wrap |
otherwise | any | NULL | Value to return on error (possibly only) |
quiet | logical | TRUE | Suppress warnings and messages |
Examples
Basic usage with safely()
safely() returns a list with two elements: result and error. If the function succeeds, result contains the output and error is NULL. If it fails, result is NULL and error contains the error condition.
library(purrr)
# Wrap a function that sometimes fails
safe_divide <- safely(function(x, y) {
if (y == 0) stop("Division by zero")
x / y
})
# Successful call
safe_divide(10, 2)
# $result
# [1] 5
#
# $error
# NULL
# Failed call
safe_divide(10, 0)
# $result
# NULL
#
# $error
# <simpleError: Division by zero>
Using possibly() for default values
possibly() is a convenience wrapper that returns a specified default value instead of an error.
library(purrr)
# Return NA on failure
safe_log <- possibly(log, otherwise = NA_real_)
# Works fine
safe_log(10)
# [1] 2.302585
# Returns NA instead of error
safe_log(-10)
# [1] NA
Combining with map()
These wrappers shine when used with map() to process multiple items where some might fail.
library(purrr)
# Vector of values to process
values <- c(100, 25, -5, 16, 0)
# Apply safely across all values
results <- map(values, safely(function(x) {
if (x <= 0) stop("Must be positive")
log(x)
}))
# Check which succeeded
succeeded <- map_lgl(results, ~is.null(.x$error))
succeeded
# [1] TRUE TRUE FALSE TRUE FALSE
# Extract successful results
map_dbl(results[succeeded], "result")
# [1] 4.605170 3.218876 2.772589
Using quietly() to capture output
quietly() captures warnings and messages without suppressing errors.
library(purrr)
quiet_sqrt <- quietly(sqrt)
# Normal call with warning
quiet_sqrt(-4)
# $result
# [1] NaN
#
# $warnings
# [1] "NaNs produced"
#
# $messages
# character(0)
#
# $result
# [1] NaN
Common Patterns
Error-tolerant data import
library(purrr)
library(readr)
# Try multiple file paths, continue on failure
file_paths <- c("data1.csv", "data2.csv", "data3.csv")
safe_read <- possibly(read_csv, otherwise = NULL)
data_list <- map(file_paths, safe_read)
valid_data <- keep(data_list, ~!is.null(.x))
Conditional processing pipelines
library(purrr)
library(dplyr)
# Process each row with potential failures
results <- df %>%
mutate(safe_result = map2(col_a, col_b, ~possibly(
function(x, y) compute_something(x, y),
otherwise = NA
)(.x, .y)))