
M06 Error Handling
Shape Rust library and application error types with thiserror, source chains, and Send + Sync for async APIs.
Overview
M06 Error Handling is an agent skill most often used in Build (also Ship review) that teaches Rust library error enums, source chains, and Result aliases versus application anyhow usage.
Install
npx skills add https://github.com/actionbook/rust-skills --skill m06-error-handlingWhat is this skill?
- Five library principles: specific types, std::error::Error, variants for matching, source chains, Send + Sync
- Worked DatabaseError enum with ConnectionFailed, QueryFailed, NotFound, and ConstraintViolation variants
- Public Result<T> alias pattern for crate-level ergonomics
- Contrasts library thiserror designs with application-oriented anyhow usage
- Examples tie connect/query/get_user flows to typed error propagation
- 5 library error-design principles
- 4+ illustrated DatabaseError variants in the example enum
Adoption & trust: 967 installs on skills.sh; 1.2k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
Your Rust crate exposes vague errors or uses anyhow everywhere, so callers cannot match failures or compose reliable async APIs.
Who is it for?
Indie developers authoring reusable Rust libraries or backend services who need agent-guided error taxonomy before publishing crates.
Skip if: Greenfield scripts where a single binary and anyhow context lines are enough and you will never expose a public API surface.
When should I use this skill?
When implementing or refactoring Rust error types for libraries versus applications, including thiserror enums and Result aliases.
What do I get? / Deliverables
You end up with typed, thiserror-backed library errors, explicit variants, and Result aliases agents can implement consistently across connect and query paths.
- Library-specific error enum with matchable variants and source chaining
- pub type Result<T> alias and connect/query-style functions using typed errors
Recommended Skills
Journey fit
Spans multiple journey phases - primary shelf plus alternate fits below.
Error-type design is core backend work when you are implementing crates and services in Rust. The skill focuses on pub enums, Result aliases, and library vs app boundaries—backend crate design, not UI or launch copy.
Where it fits
Define DatabaseError variants before agents scaffold connect and query functions in a new Rust API crate.
Audit public error surfaces for leaked strings and missing #[source] before tagging a library release.
How it compares
Procedural Rust error-design guidance, not a runtime observability or Sentry integration skill.
Common Questions / FAQ
Who is m06-error-handling for?
Rust solo builders and small teams defining public crate errors, especially when agents help implement backend modules with thiserror and std::error::Error compatibility.
When should I use m06-error-handling?
Use it in Build while designing backend crates; revisit in Ship during review when you tighten error variants, source chaining, and Send + Sync guarantees before release.
Is m06-error-handling safe to install?
It is documentation-only procedural knowledge with no bundled shell or network actions; still review the Security Audits panel on this Prism page before trusting any ingested repo.
SKILL.md
READMESKILL.md - M06 Error Handling
# Error Handling: Library vs Application ## Library Error Design ### Principles 1. **Define specific error types** - Don't use `anyhow` in libraries 2. **Implement std::error::Error** - For compatibility 3. **Provide error variants** - Let users match on errors 4. **Include source errors** - Enable error chains 5. **Be `Send + Sync`** - For async compatibility ### Example: Library Error Type ```rust // lib.rs use thiserror::Error; #[derive(Error, Debug)] pub enum DatabaseError { #[error("connection failed: {host}:{port}")] ConnectionFailed { host: String, port: u16, #[source] source: std::io::Error, }, #[error("query failed: {query}")] QueryFailed { query: String, #[source] source: SqlError, }, #[error("record not found: {table}.{id}")] NotFound { table: String, id: String }, #[error("constraint violation: {0}")] ConstraintViolation(String), } // Public Result alias pub type Result<T> = std::result::Result<T, DatabaseError>; // Library functions pub fn connect(host: &str, port: u16) -> Result<Connection> { // ... } pub fn query(conn: &Connection, sql: &str) -> Result<Rows> { // ... } ``` ### Library Usage of Errors ```rust impl Database { pub fn get_user(&self, id: &str) -> Result<User> { let rows = self.query(&format!("SELECT * FROM users WHERE id = '{}'", id))?; rows.first() .cloned() .ok_or_else(|| DatabaseError::NotFound { table: "users".to_string(), id: id.to_string(), }) } } ``` --- ## Application Error Design ### Principles 1. **Use anyhow for convenience** - Or custom unified error 2. **Add context liberally** - Help debugging 3. **Log at boundaries** - Don't log in libraries 4. **Convert to user-friendly messages** - For display ### Example: Application Error Handling ```rust // main.rs use anyhow::{Context, Result}; use tracing::{error, info}; async fn run_server() -> Result<()> { let config = load_config() .context("failed to load configuration")?; let db = Database::connect(&config.db_url) .await .context("failed to connect to database")?; let server = Server::new(config.port) .context("failed to create server")?; info!("Server starting on port {}", config.port); server.run(db).await .context("server error")?; Ok(()) } #[tokio::main] async fn main() { tracing_subscriber::init(); if let Err(e) = run_server().await { error!("Application error: {:#}", e); std::process::exit(1); } } ``` ### Converting Library Errors ```rust use mylib::DatabaseError; async fn get_user_handler(id: &str) -> Result<Response> { match db.get_user(id).await { Ok(user) => Ok(Response::json(user)), Err(DatabaseError::NotFound { .. }) => { Ok(Response::not_found("User not found")) } Err(DatabaseError::ConnectionFailed { .. }) => { error!("Database connection failed"); Ok(Response::internal_error("Service unavailable")) } Err(e) => { error!("Database error: {}", e); Err(e.into()) // Convert to anyhow::Error } } } ``` --- ## Error Handling Layers ``` ┌─────────────────────────────────────┐ │ Application Layer │ │ - Use anyhow or unified error │ │ - Add context at boundaries │ │ - Log errors │ │ - Convert to user messages │ └─────────────────────────────────────┘ │ │ calls ▼ ┌─────────────────────────────────────┐ │ Service Layer │ │ - Map between error types │ │ - Add business context │ │ - Handle recoverable errors │ └─────────────────────────────────────┘ │ │ calls ▼ ┌─────────────────