The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
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[@]dogado.de.

Error Handling Strategies

Overview

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.

library(SafeMapper)

Error Handling Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                    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                       │     │
│   └───────────────────────────────────────────────────────────────────┘     │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Layer 1: Built-in Fault Tolerance

Automatic Retry

SafeMapper automatically retries failed batches:

# Configure retry behavior
s_configure(
  retry_attempts = 5,  # Try up to 5 times per batch
  batch_size = 50      # Each batch contains 50 items
)

Retry Flow Diagram

┌─────────────────────────────────────────────────────────────────┐
│                    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                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Layer 2: Error Wrapper Functions

s_safely() - Capture Errors

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>

Using s_safely with Mapping

# 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 FALSE

s_possibly() - Default on Error

s_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

Using s_possibly for Robust Pipelines

# 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  5

s_quietly() - Capture Side Effects

s_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 "

Comparison of Error Handlers

┌─────────────────────────────────────────────────────────────────────────────┐
│                    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)    │                     │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Combining Strategies

Strategy 1: s_safely + Post-Processing

# 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

Strategy 2: s_possibly for Clean Pipelines

# 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

Strategy 3: Multi-Layer Protection

# 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 %

Decision Guide

┌─────────────────────────────────────────────────────────────────────────────┐
│                    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                │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Error Handling Patterns

Pattern 1: Fail Fast

Let errors stop execution immediately (useful during development):

# No error wrapping - errors will stop execution
# Previous batches are saved to checkpoint
results <- s_map(data, risky_function)

Pattern 2: Log and Continue

# 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

Pattern 3: Collect Errors for Reporting

# 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 %

Best Practices

┌─────────────────────────────────────────────────────────────────────────────┐
│                    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                             │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Next Steps

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.
Health stats visible at Monitor.