---
title: How to use anyhow crate
summary: Add anyhow to Cargo.toml and use Result<T> with the ? operator to simplify error handling in Rust.
lang: en
slug: how-to-use-anyhow-crate
published: 2026-04-17T12:05:42+00:00
updated: 2026-04-25T22:00:13+00:00
canonical: https://www.rustfaq.org/en/how-to-use-anyhow-crate/
---

# How to use anyhow crate



## The error boilerplate trap

You are building a command-line tool that reads a configuration file, fetches data from an API, and writes a report. In Python, you would wrap the logic in a `try/except` block and let exceptions bubble up. In Rust, you hit a wall. Every function needs a return type. Every return type needs an error type.

You start by defining `enum ConfigError`. Then you need `enum ApiError`. Your `main` function calls both, so you define `enum AppError` with variants for each. You implement `std::error::Error` for `AppError`. You implement `Display`. You write `From` conversions so the `?` operator works. You spend forty lines of code just to glue error types together. Your actual business logic is three lines. The error handling boilerplate is eating your progress.

Stop writing glue code. Let `anyhow` handle the plumbing.

## A universal error type

`anyhow` provides a single error type, `anyhow::Error`, that can hold any error. It implements the `From` trait for every type that implements `std::error::Error`. This means the `?` operator automatically converts any error into `anyhow::Error` without explicit conversion code.

Think of `anyhow::Error` like a universal adapter for electrical outlets. You do not care if the device uses a two-prong plug, a three-prong plug, or a foreign standard. You plug it into the adapter, and it works. In Rust terms, `anyhow::Error` wraps the concrete error, preserves the original type information, and adds a chain of context messages. It also captures a backtrace automatically when enabled.

The error type becomes a universal sink. Everything fits.

## Minimal setup

Add `anyhow` to your dependencies. The crate provides a `Result` type alias that saves typing.

```toml
[dependencies]
anyhow = "1"
```

In your code, import `anyhow::Result` and use it as the return type. The `?` operator propagates errors automatically.

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

fn main() -> Result<()> {
    // Result<()> is sugar for Result<(), anyhow::Error>.
    // Returning an error from main prints a report and exits.
    let value = do_work()?;
    println!("Got: {}", value);
    Ok(())
}

fn do_work() -> Result<String> {
    // Simulate a failure.
    if true {
        // anyhow! creates an error with a message and a backtrace.
        return Err(anyhow!("Something broke in do_work"));
    }
    Ok("Success".to_string())
}
```

The `?` operator does the heavy lifting. You just write the logic.

## How anyhow wraps the world

When you call `anyhow!("message")`, the macro creates an error containing the formatted message and a lazy backtrace. When you use `?` on a function returning `Result<T, E>`, Rust calls `From::from` to convert `E` into `anyhow::Error`. Since `anyhow` implements `From<E>` for all `E: std::error::Error`, this conversion happens silently.

The `anyhow::Error` type is a smart pointer to a heap-allocated error chain. It stores the source error and a list of context messages. When you print the error, `anyhow` walks the chain and displays each message, followed by the source error and the backtrace.

This design beats `Box<dyn std::error::Error>` in three ways. First, `anyhow` captures backtraces automatically when `RUST_BACKTRACE=1` is set. Second, `anyhow` provides the `Context` trait for adding messages to the chain. Third, `anyhow` avoids the trait object overhead in some hot paths by using a vtable-free representation for simple errors.

The error chain turns a cryptic failure into a debuggable report.

## Chaining context

Propagating errors with `?` works, but it loses information about where the error happened. If `fs::read_to_string` fails, you get an I/O error. You do not know which file caused the problem. Use the `Context` trait to add a message to the error chain.

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

fn run() -> Result<()> {
    // Read the config file.
    let content = fs::read_to_string("config.json")
        // context adds a message to the error chain.
        // If the read fails, the error becomes:
        // "Failed to read config: No such file or directory"
        .context("Failed to read config")?;

    // Parse the JSON.
    let config: Config = serde_json::from_str(&content)
        .context("Config file is malformed")?;

    process(config);
    Ok(())
}

#[derive(serde::Deserialize)]
struct Config {
    name: String,
}

fn process(_config: Config) {
    // Logic here.
}
```

The `Context` trait is implemented for `anyhow::Result<T>`, not for `anyhow::Error`. This is intentional. The trait returns a `Result`, so it can preserve the success type `T` while wrapping the error. You cannot add context to an error value directly; you must add it to the `Result` carrying the error.

There is a performance nuance here. The `.context("msg")` method allocates the context string immediately, even if the operation succeeds. If the context message is expensive to compute, use `.with_context(|e| format!("msg: {}", e))`. The closure runs only when an error occurs.

Context is king. An error without context is a mystery.

## Backtraces and debugging

`anyhow` captures backtraces lazily. When you create an error, the backtrace is empty. When you print the error or access the backtrace, `anyhow` captures the stack. This avoids the performance cost of capturing backtraces for every error, while still providing them when you need them.

Set the `RUST_BACKTRACE` environment variable to `1` to see backtraces in your output.

```bash
RUST_BACKTRACE=1 cargo run
```

The output includes the full stack trace for the error, pointing directly to the line where it occurred. This is invaluable for debugging complex applications.

Enable backtraces in development. They save hours.

## The library boundary rule

The Rust community has a strict convention about `anyhow`. Use `anyhow` in binary crates and application code. Never export `anyhow::Error` from a library crate.

Library crates must expose their error types so users can match on them. If your library returns `anyhow::Error`, users cannot distinguish between different error conditions. They cannot recover from specific failures. `anyhow::Error` is opaque; it hides the concrete type.

If you are writing a library, define a custom error enum and implement `std::error::Error`. Use the `thiserror` crate to generate the boilerplate. `thiserror` lets you define error variants and derive `Display` and `Error` implementations with minimal code.

Libraries define errors. Applications consume them. Keep that boundary sharp.

## Pitfalls

You cannot pattern match on `anyhow::Error`. The error type is a black box. If you try to match on it, the compiler rejects the code with E0277 (trait bound not satisfied) or a similar error about not being able to match on a boxed trait object.

```rust
use anyhow::Result;

fn handle_error(err: anyhow::Error) {
    // This does not compile.
    // match err {
    //     std::io::ErrorKind::NotFound => println!("Not found"),
    //     _ => println!("Other error"),
    // }
}
```

To recover from a specific error type inside `anyhow`, use `downcast_ref`. This checks if the error chain contains a specific type and returns a reference to it.

```rust
use anyhow::Result;
use std::io;

fn handle_error(err: anyhow::Error) {
    // Check if the error is an I/O error.
    if let Some(io_err) = err.downcast_ref::<io::Error>() {
        if io_err.kind() == io::ErrorKind::NotFound {
            println!("File not found");
            return;
        }
    }
    // Handle other errors.
    eprintln!("Error: {}", err);
}
```

Downcasting works on the source error and any context messages in the chain. It is the only way to inspect the contents of an `anyhow::Error`.

You cannot match on a black box. Handle errors where you know the type.

## Decision matrix

Use `anyhow` when you are writing an application binary and want to stop writing boilerplate error enums. Use `anyhow` when you need to chain errors with context messages using `.context()`. Use `anyhow` when you want automatic backtrace capture for debugging.

Use `thiserror` when you are writing a library and need to expose specific error variants to your users. Use `thiserror` when users of your crate need to match on error types to recover from failures.

Use `std::io::Result` when your function only deals with file system or network I/O and you do not need custom error logic. Use `std::io::Result` when you are writing low-level code that interacts with the OS and want to avoid heap allocations.

Reach for custom `enum` errors when you need to recover from specific error conditions inside the same module. Reach for custom errors when the error type carries data that the caller needs to inspect.

Pick the tool that matches your crate type. The compiler will thank you.

## Where to go next

- [How to handle errors in main function](/en/how-to-handle-errors-in-main-function/)
- [How to Implement the std::error::Error Trait](/en/how-to-implement-the-stderrorerror-trait/)
- [How to Write Panic-Free Rust Code](/en/how-to-write-panic-free-rust-code/)