
M06 Error Handling
Apply Rust library-versus-application error patterns so public crates expose typed, matchable errors instead of opaque anyhow strings.
Overview
m06-error-handling is an agent skill for the Build phase that teaches Rust library error design with typed enums, error chains, and application versus library Result patterns.
Install
npx skills add https://github.com/zhanghandong/rust-skills --skill m06-error-handlingWhat is this skill?
- Five library principles: specific types, std::error::Error, matchable variants, source chains, Send + Sync
- Contrasts library thiserror enums with application-level anyhow-style ergonomics
- Worked DatabaseError example with ConnectionFailed, QueryFailed, NotFound, and ConstraintViolation
- Public Result<T> alias pattern for a consistent crate API surface
- Shows chaining with ? and mapping missing rows to domain errors
- 5 stated library error design principles
- 4 illustrative DatabaseError variants in the example enum
Adoption & trust: 779 installs on skills.sh; 1.2k GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You are shipping a Rust crate but errors are either opaque anyhow blobs or inconsistent strings, so callers cannot match failures or trace root causes.
Who is it for?
Solo builders writing reusable Rust libraries, database adapters, or service crates that need stable, matchable error types.
Skip if: Teams that only need quick scripts with anyhow end-to-end and no public library surface to maintain.
When should I use this skill?
Refactoring or authoring Rust library APIs where errors must be specific, chainable, and matchable by downstream code.
What do I get? / Deliverables
After applying the skill, your library exposes a dedicated error enum, a public Result alias, and chain-friendly variants that binaries can map or wrap without breaking API clarity.
- Library error enum implementing std::error::Error
- pub type Result<T> alias and updated public function signatures
Recommended Skills
Journey fit
Error-type design is core backend work while implementing or refactoring Rust services and libraries. Backend crates and APIs need stable error contracts before integrations and HTTP layers are wired.
How it compares
Use for typed crate boundaries instead of defaulting every public API to anyhow::Error.
Common Questions / FAQ
Who is m06-error-handling for?
Indie and solo Rust developers building libraries, APIs, or CLIs who want callers to handle specific failure modes without losing ? ergonomics inside the crate.
When should I use m06-error-handling?
Use it during Build when defining public Result types, refactoring string errors, or preparing a crate for crates.io—especially before async HTTP or database layers land.
Is m06-error-handling safe to install?
Review the Security Audits panel on this Prism page and inspect the skill source in your repo before enabling it in automated agent runs.
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 ▼ ┌─────────────────