eyre is anyhow with a swappable handler, and it changes what your report prints
A trait object based error handling type for easy idiomatic error handling and reporting in Rust applications
At a glance
- What is it?
- eyre is a Rust error handling crate whose Report is a trait object that carries a handler chosen at install time, so the same ? and wrap_err code can produce a plain backtrace, a coloured report with a tracing span trace, or nothing extra at all. It suits applications that want rich diagnostics, and its own documentation warns libraries not to expose Report in a public API.
- Who is it for?
- Adopt eyre in an application, or a binary, where you want one error type plus optionally rich reports, since the only thing you choose is the handler at install time and the rest of the code is unchanged. Do not re-export eyre::Report from a library, which is the project's own explicit recommendation, because your error interface becomes your public API and a major bump on your side breaks your consumers.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 73 days ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The handler is the only part you choose
Most Rust code uses Result<T, E> with a concrete error enum and derives what it can. eyre replaces that with a trait object, eyre::Report, and the interesting part is not the type itself but what sits behind it.
The README describes the crate as a fork of anyhow with support for customized error reports, and the mechanism is a swappable Handler. You install a handler once at application start, and from then on every Report in the program carries whatever that handler decided to capture and how it decided to format. The code that propagates errors does not change when you swap handlers.
That inversion is what makes the companion crates possible. eyre ships as a workspace, and the Cargo.toml lists four members: eyre itself, color-eyre, color-spantrace and simple-eyre. The README names stable-eyre and jane-eyre as further known handlers, with jane-eyre described as existing purely for the pun of it and currently re-exporting color-eyre.
The practical effect is that the report format is a deployment decision. One codebase can log bare messages in a library test and print a full coloured report in a binary, without maintaining two error paths.
What each handler captures, and what you give up
The list in the README is short enough to read as a decision table.
The default handler is simple in effect. simple-eyre is described as a minimal EyreHandler that captures no additional information, for when you do not wish to capture Backtraces with errors. color-eyre captures a backtrace::Backtrace and a tracing_error::SpanTrace, adds a Help trait for attaching warnings and suggestions to the report, and pretty prints the result with the help of color-backtrace, color-spantrace and ansi_term. stable-eyre switches the backtrace type from the standard library's to backtrace-rs so it can be captured on stable Rust, while keeping the same report format as the default handler.
The difference that matters operationally is between colour-eyre and the rest. With color-eyre you get the span trace from tracing, which tells you which instrumented span the error came from, and the Help trait, which lets an error carry a suggested next step. Those are the two features that turn a stack of messages into something a user can act on.
The cost is a wider dependency surface, since color-eyre pulls in the backtrace and colour stack, and a report format that is harder to grep in logs. Choose it for a developer-facing CLI, choose the default for a service that ships errors to structured logging.
The API surface, in the order you will meet it
The README's usage notes are compact. Use Result<T, eyre::Report>, or equivalently eyre::Result<T>, as the return type of a fallible function, and use ? inside to propagate anything implementing std::error::Error:
use eyre::Result;
fn get_cluster_info() -> Result<ClusterMap> {
let config = std::fs::read_to_string("cluster.json")?;
let map: ClusterMap = serde_json::from_str(&config)?;
Ok(map)
}To add context to a low-level error, the README recommends wrapping it with a message, because an error like No such file or directory is annoying to debug without knowing which higher-level step failed. There are two forms, wrap_err for a static string and wrap_err_with for a closure that computes the message:
let content = std::fs::read(path)
.wrap_err_with(|| format!("Failed to read instrs from {}", path))?;The rendered result is a chain, which the README shows as the error line followed by a Caused by section. Downcasting is supported by value, by shared reference or by mutable reference, so you can still react to a concrete error variant:
match root_cause.downcast_ref::<DataStoreError>() {
Some(DataStoreError::Censored(_)) => Ok(Poll::Ready(REDACTED_CONTENT)),
None => Err(error),
}For ad-hoc errors, eyre! constructs a Report with string interpolation, and bail! is the shorthand for the same early return. Both also support format args captures, so eyre!("Missing attribute: {missing}") works as well.
Backtraces are off until you turn them on
A backtrace is captured and printed with the error, but it must be enabled through the standard library environment variables. The README gives the three cases explicitly.
Set RUST_BACKTRACE=1 and both panics and errors get backtraces. Set RUST_LIB_BACKTRACE=1 and only errors do. Set RUST_BACKTRACE=1 together with RUST_LIB_BACKTRACE=0 and only panics do. That last combination is the one people rarely know exists and it is the one you want if you find panics noisy but errors useful.
On nightly, the README says eyre will use the underlying error's backtrace if it has one. The tracking issue for this is rust-lang/rust#53487, which is worth knowing if you are waiting on stable for a behaviour difference.
The workspace also tunes the backtrace crate itself. In Cargo.toml, the dev profile sets opt-level 3 for the backtrace package, which is a build-cost trade: dependency capture is optimised even in development builds because you usually only take that cost once, when the error is caught, rather than per frame.
No derive macro, and that is a deliberate gap
eyre does not bundle a derive(Error) macro. The README says you can write the implementations yourself or use a standalone macro such as thiserror, and shows what a hand-written or thiserror-derived enum looks like:
use thiserror::Error;
#[derive(Error, Debug)]
pub enum FormatError {
#[error("Invalid header (expected {expected:?}, got {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("Missing attribute: {0}")]
MissingAttribute(String),
}The division of labour is clean. Concrete enums with precise variants live in the layer that can enumerate its failure modes, and anything implementing std::error::Error, including types defined in your own crate, converts into a Report through ?. You keep your typed errors where callers can downcast them, and Report is what crosses module and application boundaries.
That is also why eyre works with any error type implementing std::error::Error, including your own, rather than requiring you to adopt its error enum.
The project tells libraries not to expose Report
This is the most useful paragraph in the README for anyone designing an API, and it is a recommendation rather than a limitation.
The advice is not to re-export eyre types as part of your own public API if your library has external users. The stated reason is that your API will break if eyre ever bumps its major version and your consumers upgrade their eyre before you upgrade your own dependency.
Three deeper reasons follow. You would export an undocumented error interface that is still reachable by downcast, which makes it hard for callers to react to specific errors while not stopping them depending on details you never meant to be public. That in turn makes the error types of every library you use part of your public API, so changing any of them becomes undetectable runtime breakage. And if many of your errors are built from strings, you push callers toward string comparison, which is brittle and turns a reworded message into a silent break.
That is a specific argument rather than general conservatism. Use Report inside your application; return your own error enum from your library.
Maintenance, versions and what the manifest tells you
The workspace metadata is precise. Edition 2024, rust-version 1.85.0, licence MIT OR Apache-2.0 with both LICENSE-APACHE and LICENSE-MIT files at the root, and a resolver of 3 which matches the edition. Shared dependencies are hoisted into workspace.dependencies: indenter 0.3.0, owo-colors 4.3 and autocfg 1.0.
The dual licence is the permissive one, so you can take either, with the notice obligations that come with it, and third-party terms travel with the colour crates rather than with the core.
There are no GitHub releases for this repository, which is normal for a crate published to crates.io and means the version badge in the README points at crates.io rather than a tag. The last push was on 2026-08-11. The README routes support through a Discord channel rather than an issue tracker discussion.
The comparison worth making is with anyhow itself, since this is a fork of it. eyre earns its place when you want a tracing span trace or a Help trait attached to errors, and it costs you a decision at startup plus a dependency on whichever handler you pick.
Editorial conclusion
Adopt eyre in an application, or a binary, where you want one error type plus optionally rich reports, since the only thing you choose is the handler at install time and the rest of the code is unchanged. Do not re-export eyre::Report from a library, which is the project's own explicit recommendation, because your error interface becomes your public API and a major bump on your side breaks your consumers. Check first which handler you install, since the default captures less than color-eyre does and the difference shows up only when you are reading a report, and enable backtraces deliberately with RUST_BACKTRACE=1 or RUST_LIB_BACKTRACE=1 rather than expecting them by default.
Frequently asked questions
What is the difference between eyre and anyhow?
eyre is a fork of anyhow with support for customized error reports. It provides a trait-object error type and lets you swap the Handler to change what is captured and how the report is printed.
Which eyre report handler should I use?
The default captures the least. color-eyre captures a backtrace::Backtrace and a tracing_error::SpanTrace, adds a Help trait for warnings and suggestions, and pretty prints the report. simple-eyre captures no additional information at all, and stable-eyre switches to backtrace-rs so backtraces work on stable Rust.
How do I see backtraces with eyre?
Backtraces must be enabled through environment variables. RUST_BACKTRACE=1 gives both panics and errors backtraces, RUST_LIB_BACKTRACE=1 gives only errors, and setting RUST_BACKTRACE=1 with RUST_LIB_BACKTRACE=0 gives only panics.
Can I use eyre::Report as the error type in my library's public API?
The README recommends against it. It makes your API break on an eyre major bump, exposes an undocumented error interface that users can still downcast, and pushes callers toward string comparison for string-based errors.
Does eyre ship a derive macro for error enums?
No. The README states that no derive(Error) macro is bundled, and that you can write the std::error::Error implementations yourself or use a standalone macro such as thiserror. Any type implementing std::error::Error works with the ? operator.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/eyre-rs-eyre)Was this useful?