
M13 Domain Error
- 752 installs
- 1.3k repo stars
- Updated May 24, 2026
- zhanghandong/rust-skills
This is a copy of m13-domain-error by actionbook - installs and ranking accrue to the original listing.
m13-domain-error is a Rust agent skill that designs domain error hierarchies with recovery strategies for developers who need to separate user-facing messages from internal debug context and transient retry logic in back
About
m13-domain-error is Layer 2 design guidance in zhanghandong/rust-skills for Rust domain error handling. It asks who must handle each error and how recovery should work before defining error types, categorizing failures by audience and recoverability across user-facing, transient, and internal classes. A quick-reference table maps 5 recovery patterns—Retry with exponential backoff, Fallback defaults, Circuit Breaker via failsafe-rs, Timeout with tokio::time::timeout, and Bulkhead isolation—to concrete Rust implementations. The skill includes thiserror-based AppError hierarchies with is_retryable helpers and tokio_retry ExponentialBackoff examples. Developers reach for m13-domain-error when designing error codes, graceful degradation, or resilience patterns in production Rust APIs.
- 5-tier error categorization (user-facing, internal, system, transient, permanent)
- Decision framework answering who sees the error and whether recovery is possible
- Explicit mapping of recovery strategies including retry with backoff, fallback, circuit breaker, and graceful degradatio
- Separation of user-facing vs internal errors with tailored context and messaging
- Domain error hierarchy and error code design guidance
M13 Domain Error by the numbers
- 752 all-time installs (skills.sh)
- +7 installs in the week ending Jul 28, 2026 (Skillselion tracking)
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/zhanghandong/rust-skills --skill m13-domain-errorAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 752 |
|---|---|
| repo stars | ★ 1.3k |
| Security audit | 3 / 3 scanners passed |
| Last updated | May 24, 2026 |
| Repository | zhanghandong/rust-skills ↗ |
How do you design recoverable domain errors in Rust?
Create clear, recoverable domain error strategies that distinguish user-facing messages from internal debug info and transient retry logic.
Who is it for?
Rust backend developers implementing thiserror-based domain errors who need structured recovery instead of generic error propagation.
Skip if: Developers writing trivial CLI scripts with single exit codes or teams not using Rust error handling libraries.
When should I use this skill?
User designs domain errors, error categorization, retry with backoff, circuit breakers, or user-facing versus internal Rust errors.
What you get
A categorized Rust error enum with recovery policies, user-facing messages, and retry or circuit-breaker implementations.
- domain error enum
- recovery policy implementation
- user-facing error messages
By the numbers
- Documents 5 recovery patterns in its quick-reference table
- Classifies errors across user-facing, transient, and internal categories
- Layer 2 skill in the zhanghandong/rust-skills error-design series
Files
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 Type | Audience | Recovery | Example |
|---|---|---|---|
| User-facing | End users | Guide action | InvalidEmail, NotFound |
| Internal | Developers | Debug info | DatabaseError, ParseError |
| System | Ops/SRE | Monitor/alert | ConnectionTimeout, RateLimited |
| Transient | Automation | Retry | NetworkError, ServiceUnavailable |
| Permanent | Human | Investigate | ConfigInvalid, 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)| Question | Trace To | Ask |
|---|---|---|
| Retry policy | domain-* | What's acceptable latency for retry? |
| User experience | domain-* | What message should users see? |
| Compliance | domain-* | 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 Pattern | When | Implementation |
|---|---|---|
| Retry | Transient failures | exponential backoff |
| Fallback | Degraded mode | cached/default value |
| Circuit Breaker | Cascading failures | failsafe-rs |
| Timeout | Slow operations | tokio::time::timeout |
| Bulkhead | Isolation | separate thread pools |
Error Hierarchy
#[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
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
| Mistake | Why Wrong | Better |
|---|---|---|
| Same error for all | No actionability | Categorize by audience |
| Retry everything | Wasted resources | Only transient errors |
| Infinite retry | DoS self | Max attempts + backoff |
| Expose internal errors | Security risk | User-friendly messages |
| No context | Hard to debug | .context() everywhere |
---
Anti-Patterns
| Anti-Pattern | Why Bad | Better |
|---|---|---|
| String errors | No structure | thiserror types |
| panic! for recoverable | Bad UX | Result with context |
| Ignore errors | Silent failures | Log or propagate |
| Box<dyn Error> everywhere | Lost type info | thiserror |
| Error in happy path | Performance | Early validation |
---
Related Skills
| When | See |
|---|---|
| Error handling basics | m06-error-handling |
| Retry implementation | m07-concurrency |
| Domain modeling | m09-domain |
| User-facing APIs | domain-* |
Related skills
How it compares
Pick m13-domain-error over generic Rust error tutorials when you need recovery-policy design beyond basic Result propagation.
FAQ
What recovery patterns does m13-domain-error cover?
m13-domain-error documents 5 recovery patterns: Retry with exponential backoff, Fallback defaults, Circuit Breaker via failsafe-rs, Timeout with tokio::time::timeout, and Bulkhead thread-pool isolation.
How does m13-domain-error categorize Rust errors?
m13-domain-error classifies errors by audience and recoverability into user-facing validation errors, transient ServiceUnavailable failures, and internal errors that log details but return generic messages.
Is M13 Domain Error safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.