M13 Domain Error

by actionbook5c40d3ad7851No license1.5K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 6 weeks ago

Use when designing domain error handling. Keywords: domain error, error categorization, recovery strategy, retry, fallback, domain error hierarchy, user-facing vs internal errors, error code design, circuit breaker, graceful degradation, resilience, error context, backoff, retry with backoff, error recovery, transient vs permanent error, 领域错误, 错误分类, 恢复策略, 重试, 熔断器, 优雅降级

Instructions onlySoftware Development
AI-generated overview

Guides design of domain error handling: error categorization, recovery strategies, retries, fallbacks and error hierarchies.

What it does
This skill provides design guidance for domain error handling, centered on the question of who must handle an error and how they should recover. It offers an error categorization table (user-facing, internal, system, transient, permanent), thinking prompts, recovery patterns such as retry with backoff, fallback, circuit breaker, timeout and bulkhead, plus a sample Rust error hierarchy and retry code. It also lists common mistakes and anti-patterns and points to related skills for implementation details.
When to use it
Use it when designing error types and recovery behavior for a domain or service, especially when deciding what is user-facing versus internal, what is retryable, and what context to attach. It is also useful for reviewing existing error handling for anti-patterns such as string errors, infinite retries or exposing internal errors.
Requirements
No scripts or assets ship with the skill; it is instructions only. The examples reference Rust crates such as thiserror, anyhow, tokio-retry, backoff and failsafe-rs, but nothing needs to be installed to read the guidance.

Domain Error Strategy

Layer 2: Design Choices

Core Question

Who needs to handle this error, and how should they recover?

Before designing error types:

  • Is this user-facing or internal?
  • Is recovery possible?
  • What context is needed for debugging?

Error Categorization

Error TypeAudienceRecoveryExample
User-facingEnd usersGuide actionInvalidEmail, NotFound
InternalDevelopersDebug infoDatabaseError, ParseError
SystemOps/SREMonitor/alertConnectionTimeout, RateLimited
TransientAutomationRetryNetworkError, ServiceUnavailable
PermanentHumanInvestigateConfigInvalid, DataCorrupted

Thinking Prompt

Before designing error types:

  1. Who sees this error?

    • End user → friendly message, actionable
    • Developer → detailed, debuggable
    • Ops → structured, alertable
  2. Can we recover?

    • Transient → retry with backoff
    • Degradable → fallback value
    • Permanent → fail fast, alert
  3. What context is needed?

    • Call chain → anyhow::Context
    • Request ID → structured logging
    • Input data → error payload

Trace Up ↑

To domain constraints (Layer 3):

"How should I handle payment failures?"    ↑ Ask: What are the business rules for retries?    ↑ Check: domain-fintech (transaction requirements)    ↑ Check: SLA (availability requirements)
QuestionTrace ToAsk
Retry policydomain-*What's acceptable latency for retry?
User experiencedomain-*What message should users see?
Compliancedomain-*What must be logged for audit?

Trace Down ↓

To implementation (Layer 1):

"Need typed errors"    ↓ m06-error-handling: thiserror for library    ↓ m04-zero-cost: Error enum design
"Need error context"    ↓ m06-error-handling: anyhow::Context    ↓ Logging: tracing with fields
"Need retry logic"    ↓ m07-concurrency: async retry patterns    ↓ Crates: tokio-retry, backoff

Quick Reference

Recovery PatternWhenImplementation
RetryTransient failuresexponential backoff
FallbackDegraded modecached/default value
Circuit BreakerCascading failuresfailsafe-rs
TimeoutSlow operationstokio::time::timeout
BulkheadIsolationseparate thread pools

Error Hierarchy

rust
#[derive(thiserror::Error, Debug)]pub enum AppError {    // User-facing    #[error("Invalid input: {0}")]    Validation(String),
    // Transient (retryable)    #[error("Service temporarily unavailable")]    ServiceUnavailable(#[source] reqwest::Error),
    // Internal (log details, show generic)    #[error("Internal error")]    Internal(#[source] anyhow::Error),}
impl AppError {    pub fn is_retryable(&self) -> bool {        matches!(self, Self::ServiceUnavailable(_))    }}

Retry Pattern

rust
use tokio_retry::{Retry, strategy::ExponentialBackoff};
async fn with_retry<F, T, E>(f: F) -> Result<T, E>where    F: Fn() -> impl Future<Output = Result<T, E>>,    E: std::fmt::Debug,{    let strategy = ExponentialBackoff::from_millis(100)        .max_delay(Duration::from_secs(10))        .take(5);
    Retry::spawn(strategy, || f()).await}

Common Mistakes

MistakeWhy WrongBetter
Same error for allNo actionabilityCategorize by audience
Retry everythingWasted resourcesOnly transient errors
Infinite retryDoS selfMax attempts + backoff
Expose internal errorsSecurity riskUser-friendly messages
No contextHard to debug.context() everywhere

Anti-Patterns

Anti-PatternWhy BadBetter
String errorsNo structurethiserror types
panic! for recoverableBad UXResult with context
Ignore errorsSilent failuresLog or propagate
Box<dyn Error> everywhereLost type infothiserror
Error in happy pathPerformanceEarly validation

Related Skills

WhenSee
Error handling basicsm06-error-handling
Retry implementationm07-concurrency
Domain modelingm09-domain
User-facing APIsdomain-*

Source and attribution

Source:actionbook/rust-skillsinskills/m13-domain-errorat commit5c40d3a

License: No license

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal