The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.
SafeMapper provides multiple layers of error handling to ensure your long-running computations are robust. This guide covers both the built-in fault tolerance and the explicit error handling functions.
┌─────────────────────────────────────────────────────────────────────────────┐
│ SafeMapper Error Handling Layers │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Layer 1: Built-in Fault Tolerance │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ • Automatic checkpointing │ │
│ │ • Batch-level retry (configurable attempts) │ │
│ │ • Session recovery on re-run │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ Layer 2: Explicit Error Wrappers │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ s_safely() ──► Capture errors with result/error structure │ │
│ │ s_possibly() ──► Return default value on error │ │
│ │ s_quietly() ──► Capture messages, warnings, output │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
│ Layer 3: Custom Error Handling │
│ ┌───────────────────────────────────────────────────────────────────┐ │
│ │ tryCatch() within your function │ │
│ │ Conditional logic for expected error cases │ │
│ └───────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
SafeMapper automatically retries failed batches:
┌─────────────────────────────────────────────────────────────────┐
│ Automatic Retry Flow │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Processing Batch [1-50] │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Attempt 1 │ │
│ │ ├── Success? ─────────────► Save & Continue │
│ │ └── Error? ───────────────┐ │
│ └────────────────────────────┼────────────┘ │
│ │ │
│ ▼ (wait 1s) │
│ ┌─────────────────────────────────────────┐ │
│ │ Attempt 2 │ │
│ │ ├── Success? ─────────────► Save & Continue │
│ │ └── Error? ───────────────┐ │
│ └────────────────────────────┼────────────┘ │
│ │ │
│ ▼ (wait 1s) │
│ ┌─────────────────────────────────────────┐ │
│ │ Attempt 3 (final) │ │
│ │ ├── Success? ─────────────► Save & Continue │
│ │ └── Error? ───────────────► STOP with error │
│ └─────────────────────────────────────────┘ │
│ │
│ Note: Previous batches already saved to checkpoint │
│ On re-run: Resume from last successful batch │
│ │
└─────────────────────────────────────────────────────────────────┘
s_safely() wraps a function to capture errors instead of
throwing them:
# Create a safe version of log
safe_log <- s_safely(log)
# Successful call
result <- safe_log(10)
print(result)
#> $result
#> [1] 2.302585
#>
#> $error
#> NULL
# Error call (returns error instead of throwing)
result <- safe_log("not a number")
print(result)
#> $result
#> NULL
#>
#> $error
#> <simpleError in .f(...): non-numeric argument to mathematical function># Define function that might fail
risky_operation <- function(x) {
if (x < 0) stop("Negative values not allowed")
sqrt(x)
}
# Wrap with s_safely
safe_operation <- s_safely(risky_operation)
# Apply to data that includes problematic values
data <- c(4, -1, 9, -4, 16)
results <- s_map(data, safe_operation)
#> [20%] Processing items 1-5 of 5
#> Completed 5 items
# Extract successful results
successes <- s_map_dbl(results, ~ .x$result %||% NA_real_)
#> [20%] Processing items 1-5 of 5
#> Completed 5 items
print(successes)
#> [1] 2 NA 3 NA 4
# Check which failed
errors <- s_map_lgl(results, ~ !is.null(.x$error))
#> [20%] Processing items 1-5 of 5
#> Completed 5 items
print(errors)
#> [1] FALSE TRUE FALSE TRUE FALSEs_possibly() returns a default value when errors
occur:
# Create a function that returns NA on error
possible_log <- s_possibly(log, otherwise = NA_real_)
# Mix of valid and invalid inputs
inputs <- list(10, "text", 100, NULL, 1000)
results <- s_map_dbl(inputs, possible_log)
#> [20%] Processing items 1-5 of 5
#> Completed 5 items
print(results)
#> [1] 2.302585 NA 4.605170 NA 6.907755# Simulated data extraction that might fail
extract_value <- function(x) {
if (is.null(x) || length(x) == 0) stop("Invalid input")
x[[1]]
}
# Wrap with default value
safe_extract <- s_possibly(extract_value, otherwise = NA)
# Apply to mixed data
data <- list(
list(value = 1),
NULL,
list(value = 3),
list(),
list(value = 5)
)
results <- s_map(data, safe_extract)
#> [20%] Processing items 1-5 of 5
#> Completed 5 items
print(unlist(results))
#> [1] 1 NA 3 NA 5s_quietly() captures messages, warnings, and printed
output:
# Function with side effects
chatty_function <- function(x) {
message("Processing: ", x)
if (x > 5) warning("Large value!")
cat("Result is:", x^2, "\n")
x^2
}
# Wrap with s_quietly
quiet_function <- s_quietly(chatty_function)
# Call it - no output during execution
result <- quiet_function(7)
# Examine captured side effects
print(names(result))
#> [1] "result" "output" "warnings" "messages"
print(result$result)
#> [1] 49
print(result$messages)
#> [1] "Processing: 7\n"
print(result$warnings)
#> [1] "Large value!"
print(result$output)
#> [1] "Result is: 49 "┌─────────────────────────────────────────────────────────────────────────────┐
│ Error Handler Comparison │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Function │ On Error │ Return Structure │ Use Case │
│ ──────────────┼─────────────────┼───────────────────┼──────────────────── │
│ s_safely() │ Captures error │ list(result, │ Need to inspect │
│ │ │ error) │ what went wrong │
│ ──────────────┼─────────────────┼───────────────────┼──────────────────── │
│ s_possibly() │ Returns default │ Same as normal │ Just need results, │
│ │ │ function output │ errors = NA/default │
│ ──────────────┼─────────────────┼───────────────────┼──────────────────── │
│ s_quietly() │ Propagates │ list(result, │ Debug chatty │
│ │ error │ output, │ functions, │
│ │ │ messages, │ capture logs │
│ │ │ warnings) │ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
# Best for: When you need to analyze failures
process_item <- function(x) {
if (runif(1) < 0.3) stop("Random failure")
x^2
}
safe_process <- s_safely(process_item)
# Process with checkpointing + error capture
results <- s_map(1:10, safe_process)
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
# Analyze results
success_count <- sum(s_map_lgl(results, ~ is.null(.x$error)))
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
failure_count <- sum(s_map_lgl(results, ~ !is.null(.x$error)))
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
cat("Successes:", success_count, "\n")
#> Successes: 9
cat("Failures:", failure_count, "\n")
#> Failures: 1
# Get successful values
successful_values <- s_map_dbl(results, function(r) {
if (is.null(r$error)) r$result else NA_real_
})
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
print(successful_values)
#> [1] 1 4 9 16 25 NA 49 64 81 100# Best for: When you just need results, failures = NA
robust_sqrt <- s_possibly(
function(x) {
if (x < 0) stop("negative")
sqrt(x)
},
otherwise = NA_real_
)
# Clean pipeline
data <- c(4, -1, 9, -4, 16, -9, 25)
results <- s_map_dbl(data, robust_sqrt)
#> [14%] Processing items 1-7 of 7
#> Completed 7 items
print(results)
#> [1] 2 NA 3 NA 4 NA 5
# Easy to filter out failures
valid_results <- results[!is.na(results)]
print(valid_results)
#> [1] 2 3 4 5# Best for: Critical operations that must not fail
# Layer 1: Function-level error handling
robust_api_call <- function(x) {
tryCatch({
# Simulate API call that might fail
if (runif(1) < 0.2) stop("Temporary failure")
x * 10
}, error = function(e) {
NA_real_ # Return NA on error
})
}
# Layer 2: s_possibly for unexpected errors
safe_api_call <- s_possibly(robust_api_call, otherwise = NA_real_)
# Layer 3: SafeMapper checkpointing
results <- s_map_dbl(
1:20,
safe_api_call,
.session_id = "critical_operation"
)
#> [5%] Processing items 1-20 of 20
#> Completed 20 items
print(results)
#> [1] 10 20 30 40 50 60 70 80 90 100 110 NA 130 NA NA NA 170 180 190
#> [20] 200
cat("Success rate:", mean(!is.na(results)) * 100, "%\n")
#> Success rate: 80 %┌─────────────────────────────────────────────────────────────────────────────┐
│ Which Error Strategy to Use? │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ Q: Do you need to know WHY items failed? │
│ │ │
│ ├── YES ──► Use s_safely() │
│ │ Results contain both values and error messages │
│ │ │
│ └── NO ───► Q: Do individual failures matter? │
│ │ │
│ ├── NO ──► Use s_possibly() │
│ │ Clean results, failures become default value │
│ │ │
│ └── YES ─► Use built-in retry │
│ Configure retry_attempts for transient errors │
│ │
│ Q: Need to capture warnings/messages too? │
│ │ │
│ └── YES ──► Use s_quietly() │
│ Captures all side effects for later inspection │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
Let errors stop execution immediately (useful during development):
# Create a logging wrapper
log_errors <- function(f) {
function(...) {
tryCatch(
f(...),
error = function(e) {
message("Error: ", e$message)
NA
}
)
}
}
# Use with s_map
logged_sqrt <- log_errors(function(x) {
if (x < 0) stop("negative input")
sqrt(x)
})
results <- s_map_dbl(c(4, -1, 9, -4, 16), logged_sqrt)
#> [20%] Processing items 1-5 of 5
#> Error: negative input
#> Error: negative input
#> Completed 5 items
print(results)
#> [1] 2 NA 3 NA 4# Process and collect all errors
process_with_tracking <- function(items) {
safe_fn <- s_safely(function(x) {
if (x %% 3 == 0) stop("Divisible by 3")
x^2
})
results <- s_map(items, safe_fn)
# Build report
list(
values = s_map(results, "result"),
errors = s_map(results, "error"),
success_rate = mean(s_map_lgl(results, ~ is.null(.x$error)))
)
}
report <- process_with_tracking(1:10)
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
#> [10%] Processing items 1-10 of 10
#> Completed 10 items
cat("Success rate:", report$success_rate * 100, "%\n")
#> Success rate: 70 %┌─────────────────────────────────────────────────────────────────────────────┐
│ Error Handling Best Practices │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. Match Strategy to Need │
│ ├── Development: Let errors propagate (easier debugging) │
│ ├── Production: Use s_safely/s_possibly (robust execution) │
│ └── Critical: Multi-layer protection │
│ │
│ 2. Configure Retries Appropriately │
│ ├── Network operations: 3-5 retries │
│ ├── Local computation: 1 retry (errors usually persistent) │
│ └── Rate-limited APIs: Consider exponential backoff │
│ │
│ 3. Log Errors for Analysis │
│ ├── Use s_safely to capture error details │
│ ├── Store error counts/types for monitoring │
│ └── Alert on error rate thresholds │
│ │
│ 4. Test Error Handling │
│ ├── Intentionally inject failures │
│ ├── Verify checkpoint/recovery works │
│ └── Check that all error cases are handled │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.