purrr::possibly
possibly() wraps a function so it returns a default value instead of crashing when something goes wrong. It’s purrr’s adverb for handling errors without full try-catch machinery.
Basic Usage
library(purrr)
# log("a") fails — log of a string is not numeric
list("a", 10, 100) |>
map_dbl(possibly(log, NA_real_))
# [1] NA 2.302585 4.605170
possibly(log, NA_real_) creates a version of log that returns NA_real_ instead of throwing an error.
Signature
possibly(.f, otherwise = NULL, quiet = TRUE)
.f— the function to wrapotherwise— value to return on error (defaultNULL)quiet— ifTRUE(default), errors are suppressed; ifFALSE, errors print as they occur
Choosing an Appropriate Default
The otherwise value should match the type of what you’re working with:
# Numeric data — use NA_real_
x |> map_dbl(possibly(mean, NA_real_))
# Character data — use NA_character_
names |> map_chr(possibly(toupper, NA_character_))
# Logical — use NA
flags |> map_lgl(possibly(isTRUE, NA))
If otherwise is NULL (the default), failed elements are dropped from the output:
list("a", 10, 100) |>
map(possibly(log)) |>
list_c()
# [1] 2.302585 4.605170
The string "a" is gone — list_c() only sees the successful results.
Quiet vs Noisy
By default, quiet = TRUE hides errors silently. Set quiet = FALSE to see them as they happen:
x <- list(1, "b", 3)
possibly(log, NA_real_, quiet = FALSE)(1)
# [1] 0
possibly(log, NA_real_, quiet = FALSE)("b")
# Error in log("b") : non-numeric argument to binary operator
# [1] NA
Useful during development or when you want a log of what failed.
Combining with map
possibly() shines inside map:
library(dplyr)
# Get row means for each group, but some groups are empty
mtcars |>
group_by(cyl) |>
group_modify(~ {
result <- possibly(mean, NA_real_)(.$mpg)
tibble(mean_mpg = result)
})
possibly vs safely
These are counterparts:
| Feature | safely() | possibly() |
|---|---|---|
| Return on error | list with result and error | your chosen default |
| Typical use | when you need to inspect the error | when you just want a fallback |
| Output type | always a list | can be simplified with list_c(), map_dbl(), etc. |
# safely — gives you both result and error
safe_log <- safely(log)
safe_log("a")
# $result
# NULL
# $error
# <simpleError ...>
# possibly — gives you a fallback
possibly(log, NA_real_)("a")
# [1] NA
Use possibly() when you don’t care why something failed — you just want a sensible value instead.
Real Example: API Calls
library(httr)
fetch_page <- possibly(\(url) {
GET(url) |>
content(as = "text")
}, otherwise = NA_character_)
urls <- c(
"https://api.example.com/data/1",
"https://api.example.com/data/2",
"https://api.example.com/data/3"
)
pages <- urls |>
map_chr(fetch_page)
# NA for failed requests, clean text for successful ones
See Also
- /reference/tidyverse/purrr-safely/ — wraps a function to return a list with result and error
- /reference/tidyverse/purrr-map/ — transform each element with
map() - /guides/purrr-functional-programming/ — functional programming patterns with purrr