Library / SDK
dtolnay/anyhow avatar
dtolnay/anyhow

dtolnay/anyhow: A Concrete Error Type for Rust Applications

Flexible concrete Error type built on std::error::Error

6,664 stars226 forksRustApache-2.0

At a glance

What is it?
Anyhow gives Rust applications one error type that accepts every std::error::Error, with context chains and downcasting. It is the right tool for binaries and the wrong tool for library public APIs.
Who is it for?
Adopt anyhow in application code, binaries, CLI tools and services where the caller of your function is a human reading a log line, not another crate. Do not adopt it as the public error type of a library you publish: the README's own comparison says a library that wants to control exactly what the caller receives should design its own error type, which is what thiserror is for.
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 2 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 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The Rust error problem anyhow was written to remove

A Rust function that can fail from several different causes has to name one error type in its signature. If it reads a file and parses JSON, the two underlying errors are std::io::Error and serde_json::Error, and there is no single standard type that holds both without boxing. Application authors end up writing enum wrappers with From impls for every source error, or writing Box<dyn std::error::Error>, which loses the ability to attach context and reads badly in signatures.

anyhow::Error is a trait object based error type, in the README's words, built on std::error::Error rather than on a separate trait. That last part matters historically: the README compares it to failure, noting that failure defined its own failure::Fail trait while anyhow relies on the standard library trait after RFC 2504 made that workable. The audience is application code. A CLI that talks to an API, a daemon that reads config, a build script: places where the error is printed for a human and the exact type is nobody's contract.

How anyhow::Error carries a cause chain and stays downcastable

The type is used through anyhow::Result<T>, an alias for Result<T, anyhow::Error>. Inside such a function the ? operator converts any error implementing std::error::Error into anyhow::Error, so a std::fs::read_to_string call and a serde_json::from_str call can sit in the same function without a hand-written conversion.

Context is the second half of the mechanism. The Context trait adds .context(...) and .with_context(...) to a Result, wrapping the original error rather than replacing it. The README shows the printed result: an outer line reading Error: Failed to read instrs from ./path/to/instrs.json, then a Caused by: line with No such file or directory (os error 2). The chain is preserved, so the message a user sees names the operation and the underlying cause stays underneath it.

Because the concrete error is still stored inside, downcasting works by value, by shared reference, or by mutable reference. The README's example matches on root_cause.downcast_ref::<DataStoreError>() to detect a specific variant and return redacted content instead of propagating. That is the escape hatch from type erasure: generic at the boundary, specific where the code needs to branch.

Backtraces are captured when the compiler is Rust 1.65 or newer and the underlying error does not already provide one. Visibility is controlled by environment variables, and the README lists three combinations: RUST_BACKTRACE=1 for panics and errors, RUST_LIB_BACKTRACE=1 for errors only, and RUST_BACKTRACE=1 with RUST_LIB_BACKTRACE=0 for panics only.

Adding anyhow to a crate and returning your first contextual error

The README gives the dependency line directly. Add it to the [dependencies] table of Cargo.toml and let Cargo resolve the 1.0 series.

toml
[dependencies]
anyhow = "1.0"

A fallible function then returns anyhow::Result and uses ? on anything implementing std::error::Error. The README's cluster example reads a file and parses it in two lines, with no error enum in sight.

rust
use anyhow::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 attach the operation name, import Context and call .context or .with_context. The closure form is the one to prefer when the message needs a value from the surrounding scope.

rust
use anyhow::{Context, Result};

fn main() -> Result<()> {
    let content = std::fs::read(path)
        .with_context(|| format!("Failed to read instrs from {}", path))?;
    Ok(())
}

Running that against a missing file prints the two-part output described above, with the formatted message first and the OS error under Caused by. For a one-off failure with no underlying cause, the anyhow! macro builds an error from a format string, and bail! is the shorthand for returning one immediately.

rust
use anyhow::bail;

fn check(missing: &str) -> anyhow::Result<()> {
    bail!("Missing attribute: {}", missing);
}

main returning Result<()> is the convention that makes this work: the runtime prints the error chain and exits non-zero when the function returns Err.

Where anyhow is the wrong error type

The README draws the boundary itself in the thiserror comparison: use anyhow if you do not care what error type your functions return, and use thiserror if you are a library that wants the caller to get exactly the information you choose. A public library API returning anyhow::Error tells downstream users nothing about which failures are possible. They cannot write an exhaustive match, because the variants are erased. Every caller that needs to react differently to different failures has to downcast and handle the None case, which is exactly the ceremony the library was supposed to have designed away.

There is a second constraint in the manifest. The crate declares rust-version = "1.68", so a project pinned to an older toolchain cannot use it, and the README notes that in no_std mode, with Rust older than 1.81, a non-anyhow error inside a function returning anyhow's type may need an explicit .map_err(Error::msg). That is a real papercut for embedded and kernel-adjacent code, and no_std also requires a global allocator.

One more thing worth knowing before you copy an old Cargo.toml: the backtrace feature still exists but the manifest says plainly that it has no effect, kept only for compatibility with the old optional dependency. Enabling it does nothing today.

anyhow versus thiserror, and what each one actually changes

The two crates come from the same author and are designed to be used together, but they solve opposite halves of the problem. thiserror is a derive macro. You declare an enum, annotate variants with #[error("...")] format strings, and the macro writes the Display and std::error::Error impls. The result is a named type with named variants, which is what a library wants to expose.

anyhow is the opposite trade. It gives you one type that accepts everything and lets you defer the question of what went wrong until the point where you print or inspect it. The README's own example shows the split in one file: define FormatError with thiserror, then let anyhow carry it through application code, because anyhow works with any type implementing std::error::Error, including ones defined in your own crate. It does not bundle a derive macro, so you either write the impls by hand or bring thiserror in as a separate dependency.

The practical rule that falls out of the README: thiserror at the edges of a library, anyhow in the main function and the layers above it.

Release cadence, licence and the cost of upgrading

The repository is not archived, and the last push was on 2026-08-22. Recent releases in the 1.0 series are 1.0.104 on 2026-07-18, 1.0.103 on 2026-06-25 and 1.0.102 on 2026-02-20, so the patch line moves at a moderate pace rather than constantly. Because the crate has stayed on 1.0, a dependency written as anyhow = "1.0" picks up patch releases through a normal cargo update, and the semver contract means no source changes should be required. The upgrade cost is therefore close to zero for most users, with one caveat: the declared rust-version = "1.68" can be raised in a future release, and Cargo will refuse to build on an older toolchain when it is.

Licensing is dual. The manifest declares license = "MIT OR Apache-2.0", and the repository carries LICENSE-APACHE and LICENSE-MIT at the top level, matching what the README states. The README also notes that contributions are dual licensed under the same terms unless stated otherwise. For a commercial product, the usual Rust convention applies: you choose one of the two licenses and comply with its terms, which for MIT means retaining the copyright notice and permission text. This is a description of what the files say, not legal advice; if your organisation has a policy on dual-licensed dependencies, the two files in the repository root are the ones to read.

Editorial conclusion

Adopt anyhow in application code, binaries, CLI tools and services where the caller of your function is a human reading a log line, not another crate. Do not adopt it as the public error type of a library you publish: the README's own comparison says a library that wants to control exactly what the caller receives should design its own error type, which is what thiserror is for. Before committing, check that your toolchain meets the rust-version = "1.68" declared in Cargo.toml, and if you build for no_std, confirm your Rust version, because the README notes that versions older than 1.81 may need an extra .map_err(Error::msg) when a non-anyhow error is converted inside a function returning anyhow's type.

Frequently asked questions

What is anyhow in Rust used for?

It provides anyhow::Error, a trait object based error type for idiomatic error handling in Rust applications, so a fallible function can return anyhow::Result and use ? on any error implementing std::error::Error. The README positions it for application code rather than library APIs.

How do you use anyhow in Rust?

Add anyhow = "1.0" to your dependencies, return anyhow::Result from fallible functions, and use ? to propagate errors. Import Context to add .context or .with_context messages, which are printed above the Caused by line.

What does anyhow do in Rust?

It converts any error implementing std::error::Error into a single concrete type, preserves the cause chain when context is attached, and supports downcasting by value, shared reference or mutable reference. It also captures a backtrace on Rust 1.65 and newer when backtraces are enabled through the documented environment variables.

What is anyhow used for in general?

In this crate's terms, it is used as the return error type of application functions, including main, where the goal is easy propagation and a readable printed chain instead of a designed error enum.

How do you use the word anyhow?

In Rust code the name is used as a crate and a type: anyhow = "1.0" in Cargo.toml, anyhow::Result as a return type, and anyhow! or bail! to build a one-off error message from a format string.

What is anyhow?

It is a Rust crate that provides a flexible concrete Error type built on std::error::Error, intended to make error propagation and context attachment straightforward in application code.

Official sources

  1. dtolnay/anyhow on GitHub
  2. Issues
  3. License: Apache-2.0
  4. README
  5. Releases
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.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/dtolnay-anyhow.svg)](https://hysenlabs.com/projects/dtolnay-anyhow)