Error Reporting
Present actionable errors to CLI users with anyhow and miette.
Busca en todas las páginas de la documentación
Present actionable errors to CLI users with anyhow and miette.
use anyhow::Context;
fn read_config(path: &str) -> anyhow::Result<String> {
std::fs::read_to_string(path)
.with_context(|| format!("failed to read config at {path}"))
}
fn main() -> anyhow::Result<()> {
let cfg = read_config("app.toml")?;
println!("loaded {cfg}");
Ok(())
}When to reach for this: Application binaries where users need context-rich messages, not library-style typed errors.
use miette::{miette, IntoDiagnostic, Result};
#[derive(Debug, thiserror::Error, Diagnostic)]
#[error("invalid port: {0}")]
#[diagnostic(code(app::invalid_port))]
struct InvalidPort(u16);
fn parse_port(s: &str) -> Result<u16> {
let port: u16 = s.parse().into_diagnostic()?;
if port == 0 {
return Err(miette!(InvalidPort(port)));
}
Ok(port)
}
fn main() -> Result<()> {
let port = parse_port("0")?;
println!("listening on {port}");
Ok(())
}What this demonstrates:
anyhow::Context adds location context to std errorsmiette provides diagnostic codes and formatted reportsthiserror defines small domain error typesmain returns Result for automatic non-zero exit| Crate | Role |
|---|---|
anyhow | Ergonomic Result in binaries |
thiserror | Typed errors in libraries |
miette | Beautiful reports with source spans |
use std::process::ExitCode;
fn main() -> ExitCode {
if let Err(e) = run() {
eprintln!("{e:#}");
return ExitCode::FAILURE;
}
ExitCode::SUCCESS
}Use {:#} for the full anyhow chain.
Display only - Users miss the error chain. Fix: Use {:#} or miette::Report.Result and reserve panics for bugs.thiserror; binaries wrap with anyhow.--verbose or env hint./home/ci/... confuses users. Fix: Strip or relativize paths in user messages.| Alternative | Use When | Don't Use When |
|---|---|---|
thiserror only | Libraries with matchable errors | Top-level binary needing context chains |
Plain eprintln! | Tiny scripts | Multi-step pipelines needing context |
color-eyre | Dev tools during development | Minimal dependency production CLIs |
Avoid it. Library consumers need typed errors. Use thiserror and let binaries wrap with anyhow.
Use miette::Diagnostic with a code(...) attribute for searchable error IDs.
Define an ErrorResponse { code, message, details } struct and print serde JSON on failure when --json is set.
Warnings go to stderr with WARN: prefix. Errors use non-zero exit and distinct formatting.
Use fluent or rust-i18n for translated messages. Keep error codes stable across locales.
Assert on err.to_string() or format!("{err:#}") in integration tests.
Log full detail with tracing::error!. Print a short user-facing summary to stderr.
Collect errors in a Vec and print all before exiting. Common in linters and validators.
anyhow supports .context() and #[source]. Each layer adds operator-relevant detail.
miette works on stable Rust. Enable fancy feature for graphical reports when stderr is a TTY.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Revisado por Chris St. John·Última actualización: 16 jul 2026