Skip to content
Agentmart
Submit a tool

rust-error-handling

Rust error-handling patterns and anti-patterns -- `unwrap()` in request hot paths, `panic!` instead of returning a `Result`, leaking error internals via…

Skill by MILTONADINA

IndexedMIT
Write a review

Install

Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.

npx agentmarket install skill/miltonadina-rust-error-handling

Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

Rust error-handling patterns and anti-patterns -- `unwrap()` in request hot paths, `panic!` instead of returning a `Result`, leaking error internals via `Debug` derive on error types containing privileged state. Patterns covered include `thiserror` for typed errors, `anyhow` for application-layer error propagation, and error-payload sanitization at API boundaries. Triggers on Rust error-prone code authoring (`unwrap`, `expect`, `panic!`, `Result<_, _>` patterns). General security hygiene at the language-runtime layer.

Use cases: backend, security

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

Permissions

What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.

Tools
No tools found
Secrets
None asked for
Install scripts
None found

Network hosts it talks to 1

  • codeload.github.com · found in install command

Files it touches

  • ~/.claude/skills/rust-error-handling

What's inside

Skill name
rust-error-handling
Description
Rust error-handling patterns and anti-patterns -- `unwrap()` in request hot paths, `panic!` instead of returning a `Result`, leaking error internals via `Debug` derive on error types containing privileged state. Patterns covered include `thiserror` for typed errors, `anyhow` for application-layer error propagation, and error-payload sanitization at API boundaries. Triggers on Rust error-prone code authoring (`unwrap`, `expect`, `panic!`, `Result<_, _>` patterns). General security hygiene at the language-runtime layer.
License
MIT
Instructions
280 lines in SKILL.md · view on GitHub

Agent Skills spec issues

  • name: "rust-error-handling" does not match folder "error-handling"

Files (1)

  • SKILL.md

SKILL.md (first lines)

# Rust Error Handling

> This skill addresses **general security hygiene at the
> language-runtime layer** -- the patterns close common Rust failure
> modes (request-handler panics, leaky error responses, internal-state
> exposure via `Debug`) but they are NOT the load-bearing defense
> against any specific OWASP ASI / AST threat. The agent-specific
> defenses operate at different layers: prompt-injection-defense for
> ASI01/ASI04, Tool Misuse defenses (e.g., webhook idempotency) for
> ASI02. Rust error handling is the substrate that lets the OTHER
> defenses work without panicking the whole process.

**Tradeoff:** `unwrap()` is shorter than proper `Result` handling.
Each `unwrap` in a request path is a future production panic. The
discipline below trades line-count for crash-resistance.

---

## Why this matters

A Rust web service that panics on `unwrap()` fails the request. In a
`tokio` runtime the panic unwinds only the task serving that request:
the runtime catches it and every other task keeps running, and the
client receives a 500 only if a layer such as tower-http's
`CatchPanicLayer` converts the panic into a response. Built with
`panic = "abort"`, the same panic terminates the whole process. The
downstream effects are: lost requests, unclear
error attribution in logs, and -- if the panic's payload contains
privileged state (a database connection string, a JWT, a customer
email) -- accidental disclosure when the panic is logged.

The mitigation is structural: convert `unwrap()` / `panic!` paths to
`Result` returns that the caller can handle deliberately, derive
`Debug` carefully on error types that may carry sensitive payloads,
and sanitize the error response at the API boundary.

---

## Pattern 1 -- Typed errors with `thiserror`

Define one error enum per crate (or per logical module) using
`thiserror`. The enum variants name the failure modes the code can
encounter; their `Display` impls produce log-safe messages.

```rust
use thiserror::Error;

#[derive(Debug, Error)]
pub enum CheckoutError {
    #[error("payment provider rejected the charge")]
    PaymentRejected,

    #[error("order {order_id} not found")]
    OrderNotFound { order_id: String },

    #[error("database error")]
    Database(#[from] sqlx::Error),

    #[error("internal: {0}")]
    Internal(String),
}
```

Each handler returns `Result<T, CheckoutError>` and uses `?` for
propagation. The caller (API handler, CLI command) maps the typed
error to an HTTP status / exit code in one place.

```rust
pub async fn checkout(order_id: String) -> Result<Receipt, CheckoutError> {
    let order = db::find_order(&order_id).await?;  // ? propagates Database errors
    let order = order.ok_or(CheckoutError::OrderNotFound { order_id })?;
    let receipt = payment::charge(&order).await
        .map_err(|_| CheckoutError::PaymentRejected)?;
    Ok(receipt)
}
```

This pattern keeps the happy path readable AND maintains structured
error categories for downstream handling.

---

## Pattern 2 -- `anyhow` for application-layer error propagation

For binary crates (CLIs, services) where typed errors would add ceremony
without value, use `anyhow::Result<T>`. It wraps any error type and
provides `.context(...)` for adding human-readable trace info.

```rust
use anyhow::{Context, Result};

pub async fn run(args: Args) -> Result<()> {
    let config = load_config(&args.config_path)
        .with_context(|| format!("failed to load config from {}", args.config_path))?;

    let pool = db::connect(&config.database_url).await
        .context("failed to connect to database")?;

    for migration in args.migrations {
        apply(&pool, migration).await
            .with_context(|| format!("migration {migration} failed"))?;
    }

    Ok(())
}
```

The `.context(...)` calls build a stack of trace messages. When an
error finally bubbles to `main()`, printing it produces a multi-line
trace that points at the operation, not just the leaf cause. This is
the closest Rust gets to Python tracebacks for application code.

`anyhow` is for application crates. Library crates should prefer
`thiserror` so consumers can pattern-match specific error variants.

---

## Pattern 3 -- Sanitize error payloads at the API boundary

From MILTONADINA/DevOPs, MIT.

More from MILTONADINA/DevOPs

Signals

Stars
0
Forks
0
Open issues
12
Last commit (this folder)
Sep 25, 2026 (1 month ago)
Last release
Sep 23, 2026 (1 month ago)

From GitHub (fetched Oct 24, 2026).

Details

License
MIT
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from MILTONADINA

Cite this page

rust-error-handling skill. Agent Mart. https://agentmart.dev/skills/miltonadina-rust-error-handling (last updated 2026-10-24)