What it is
thiserror derives the std::error::Error implementation for your custom error enums, including display messages and source chaining.
Derive Error on an enum and annotate each variant with a display message. #[from] generates conversions so the ? operator works across error types.
Installation
cargo add thiserrorGetting started
The smallest useful thing you can do with it, and what each part means.
rust
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("could not read {path}")]
Io {
path: String,
#[source] source: std::io::Error,
},
#[error("invalid TOML")]
Parse(#[from] toml::de::Error),
#[error("missing required key `{0}`")]
MissingKey(String),
}
fn load(path: &str) -> Result<Config, ConfigError> {
let text = std::fs::read_to_string(path)
.map_err(|source| ConfigError::Io { path: path.into(), source })?;
Ok(toml::from_str(&text)?) // converts via #[from]
}Advanced usage
Where the library earns its place over a simpler alternative.
rust
match load("app.toml") {
Ok(config) => run(config),
Err(ConfigError::Io { source, .. })
if source.kind() == std::io::ErrorKind::NotFound => run(Config::default()),
Err(e) => {
eprintln!("{e}");
// Walk the chain for the full context.
let mut cause = e.source();
while let Some(c) = cause {
eprintln!(" caused by: {c}");
cause = c.source();
}
}
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- ? does not convert the error type
- Add #[from] to the variant wrapping that error type — a plain field does not generate the conversion.
- Two variants both use #[from] for the same type
- Only one From impl per type is possible. Use #[source] with an explicit constructor for the second.
Best practices
- Use thiserror in libraries and anyhow in binaries — that split is the ecosystem convention.
- Add #[source] or #[from] so the underlying cause is preserved.
- Write display messages in lower case without trailing punctuation; they are composed into chains.
- Do not leak internal error types through your public API unless they are part of the contract.
Background
Why it exists, and what it was reacting to.
From David Tolnay, thiserror is the library half of Rust's error-handling convention: define precise error types for a library, and use anyhow in the application that consumes it.
