Announcing error-chain, a library for consistent and reliable Rust error handling


The error-chain crate (docs) is a new crate for dealing with Rust error boilerplate. It provides a few unique features:

  • No error is ever discarded. This library primarily makes it easy to “chain” errors with the chain_err method.
  • Introducing new errors is trivial. Simple errors can be introduced at the error site with just a string.
  • Errors create and propagate backtraces.

I think the lack of the above are widespread problems with Rust error handling, so I’m interested to hear what people think about this solution. It is inspired by quick-error (and in fact includes a hacked up version of it for internal use) as well as Cargo’s internal error handling techniques. This library is used by rustup for error handling.

One note about usage that isn’t included in the docs: the chain_error! macro recurses deeply, so you’ll probably need to use the little-known #![recursion_limit = "1024"] macro on crates that import it.

For detailed usage information read the docs, which are reproduced in part below.

Declaring error types

Generally, you define one family of error types per crate, though it’s also perfectly fine to define error types on a finer-grained basis, such as per module.

Assuming you are using crate-level error types, typically you will define an errors module and inside it call error_chain!:

// Define the error types and conversions for this crate
error_chain! {
    // The type defined for this error. These are the conventional
    // and recommended names, but they can be arbitrarily chosen.
    types {
        Error, ErrorKind, ChainErr, Result;

    // Automatic conversions between this error chain and other
    // error chains. In this case, it will e.g. generate an
    // `ErrorKind` variant called `Dist` which in turn contains
    // the `rustup_dist::ErrorKind`, with conversions from
    // `rustup_dist::Error`.
    // This section can be empty.
    links {
        rustup_dist::Error, rustup_dist::ErrorKind, Dist;
        rustup_utils::Error, rustup_utils::ErrorKind, Utils;

    // Automatic conversions between this error chain and other
    // error types not defined by the `error_chain!`. These will be
    // boxed as the error cause and wrapped in a new error with,
    // in this case, the `ErrorKind::Temp` variant.
    // This section can be empty.
    foreign_links {
        temp::Error, Temp,
        "temporary file error";

    // Define additional `ErrorKind` variants. The syntax here is
    // the same as `quick_error!`, but the `from()` and `cause()`
    // syntax is not supported.
    errors {
        InvalidToolchainName(t: String) {
            description("invalid toolchain name")
            display("invalid toolchain name: '{}'", t)

This populates the the module with a number of definitions, the most important of which are the Error type and the ErrorKind type. They look something like the following:

use std::error::Error as StdError;
use std::sync::Arc;

pub struct Error(pub ErrorKind,
                 pub Option<Box<StdError + Send>>,
                 pub Arc<error_chain::Backtrace>);

impl Error {
    pub fn kind(&self) -> &ErrorKind { ... }
    pub fn into_kind(self) -> ErrorKind { ... }
    pub fn iter(&self) -> error_chain::ErrorChainIter { ... }
    pub fn backtrace(&self) -> &error_chain::Backtrace { ... }

impl StdError for Error { ... }
impl Display for Error { ... }

pub enum ErrorKind {

This is the basic error structure. You can see that ErrorKind has been populated in a variety of ways. All ErrorKinds get a Msg variant for basic errors. When strings are converted to ErrorKinds they become ErrorKind::Msg. The “links” defined in the macro are expanded to Dist and Utils variants, and the “foreign links” to the Temp variant.

Both types come with a variety of From conversions as well: Error can be created from ErrorKind, from &str and String, and from the “link” and “foreign_link” error types. ErrorKind can be created from the corresponding ErrorKinds of the link types, as wall as from &str and String.

into() and From::from are used heavily to massage types into the right shape. Which one to use in any specific case depends on the influence of type inference, but there are some patterns that arise frequently.

Chaining errors

This is the focus of the crate’s design. To extend the error chain:

use errors::ChainErr;
try!(do_something().chain_err(|| "something went wrong"));

chain_err can be called on any Result type where the contained error type implements std::error::Error + Send + 'static. If the Result is an Err then chain_err evaluates the closure, which returns some type that can be converted to ErrorKind, boxes the original error to store as the cause, then returns a new error containing the original error.

The above example turns a string into an error, but you could also write e.g.

try!(do_something().chain_err(|| ErrorKind::Foo));

Returning new errors

Introducing new error chains, with a string message:

fn foo() -> Result<()> {
    Err("foo error!".into())

Introducing new error chains, with an ErrorKind:

fn foo() -> Result<()> {

Note that the return type is is the typedef Result, which is defined by the macro as pub type Result<T> = ::std::result::Result<T, Error>. Note that in both cases .into() is called to convert a type into the Error type: both strings and ErrorKind have From conversions to turn them into Error.

When the error is emitted inside a try! macro or behind the ? operator, then the explicit conversion isn’t needed, since the behavior of try! will automatically convert Err(ErrorKind) to Err(Error). So the below is equivalent to the previous:

fn foo() -> Result<()> {

fn bar() -> Result<()> {

Foreign links

Errors that do not conform to the same conventions as this library can still be included in the error chain. They are considered “foreign errors”, and are declared using the foreign_links block of the error_chain! macro. Errors are automatically created from foreign errors by the try! macro.

Foreign links and regular links have one crucial difference: From conversions for regular links do not introduce a new error into the error chain, while conversions for foreign links always introduce a new error into the error chain. So for the example above all errors deriving from the temp::Error type will be presented to the user as a new ErrorKind::Temp variant, and the cause will be the original temp::Error error. In contrast, when rustup_utils::Error is converted to Error the two ErrorKinds are converted between each other to create a new Error but the old error is discarded; there is no “cause” created from the original error.


The earliest non-foreign error to be generated creates a single backtrace, which is passed through all From conversions and chain_err invocations of compatible types. To read the backtrace just call the backtrace() method.


The iter method returns an iterator over the chain of error boxes. See how rustup uses this during error reporting.

Application error messages that are friendly and detailed

Nice. Is it nightly only?


Oh. Oh wow. This is going to remove so much boilerplate from my “standard Rust crate” template, and make it more compatible with other crates doing the same thing.


When you say backtraces, do you mean line number information? Or more of a “semantic backtrace”?


Both as far as I can tell. There is an actual line number / symbol backtrace which uses the backtrace crate, but there is also the ‘semantic’ backtrace of cause-s which you can [iterate](( over using iter


If this relies on stack walking, then I would expect it to be somewhat inaccurate in Release mode or when Debug symbols are stripped.

Still, having used a similar solution C++, it’s so much more useful than nothing!


@dpc I believe it works on stable, but have not actually tested personally. I know rustup as a whole can build on stable but I haven’t done it.

@nikomatsakis Actual stack traces, with whatever information the backtrace crate can pull out of the debugging info.

Yeah, it’s going to give you whatever quality of backtrace libbacktrace is capable of.


Sorry if it’s stupid question, is there any reason you can’t make Error generic over ErrorKind ? (I guess there some coherence issues?)

If it’s not possible, same question for ChainErr trait. It will be tricky if you would ever need to use chain_err from two different errors in the same module (Not sure how common is it).

And wrapping Result trait looks like needless. Making a type alias is just a one-liner in plain Rust (i.e. less magic in macro is good thing in my opinion)


Is it possible to disable the backtrace feature? After reading Joe Duffy’s thoughts on error-handling, I am fairly well convinced that backtraces are only of interest for debugging fatal errors, which is very separate from programmatic handling of recoverable error conditions. I can certainly see how a backtrace would be interesting during development work while recoverable error cases are not yet handled, but I don’t think “generate a backtrace by default whenever we stray from the happy path” is likely to be a helpful mindset for making a robust final product.

(Ideally, I’d like to be able to enforce a project-wide configuration that would have backtraces included in debug mode but not release mode.)


Forgive me, it’s been a month since I worked on this code so I’ve forgotten some, but it looks to me like there would be coherence violations in the From conversions, e.g.

impl From<$link_error_path> for $error_name {
    fn from(e: $link_error_path) -> Self {
        $error_name($error_kind_name::$link_variant(e.0), e.1)

I tried this to start and couldn’t formulate it in a way that worked with coherence. As long as you only create one type of error per crate the scenario you describe of needing multiple ChainErr shouldn’t happen.

It’s not a wrapper, it’s a type alias. A person could write it as a one liner, but I figured since I was doing all the other definitions, and it’s common practice to do the type alias, the macro should just do it.

It’s not currently, but I can see it might be worth while. Personally, even if it’s not useful in some scenarios I wouldn’t have a desire to turn it off. The reason I see to disable it is to save the work of generating it, but I also consider it a design tradeoff that (in this library at least) performance on the error path is not critical (lots of boxing in this strategy).

I think I agree with that too, but I would also suggest that backtraces are useful for debugging unexpected errors. It’s reasonably common to get bug reports that show an error that my program printed, and for me to have no idea why that would happen. If I can say ‘will you run that again with backtraces on?’ it will help me figure out what happened.


Oh, yes, absolutely. (That’s actually a much better statement than my “only of interest for debugging fatal errors”–I think what I’m really trying to get at is unexpect errors should be fatal if possible.) But how can an error be unexpected if it’s part of the signature of a method? If you don’t expect a particular error condition, panic when you see it (again as per Duffy’s advice). In my ideal world, once a software product is ready for release, errors that can be handled are, while errors representing true unforseen bugs and conditions that were (incorrectly) assumed to be impossible are “handled” by panicking (if indeed they’re handled at all, since the whole point is that this category of errors can’t be predicted in advance).

I suppose I can think of a very few cases where generating some backtrace information and propagating it via error-types might be useful, even in release mode. One is if there’s some way to panic inside the error-handling code several levels up the call-chain; in this case you’ll lose some potentially-crucial backtrace information if the “semi-happy-path” error-recovery mechanism doesn’t propagate the trace info “just in case”. But that seems (prima facie) like a pretty limited scenario to me.


Awesome! I just converted one of my codegen projects with a bunch of different possible error types over to error_chain, it was really simple. Not a single impl Display in sight :smile:


Glad to hear it!


I just wanted to add my kudos, this is excellent. I started working on a much lamer version of this macro, and am in the progress of switching. Excellent job.

A note to other beginners, since this generates so many structs and impls, you might be wondering (like #[derive(…)]) how to “view” the structs. The easiest method i’ve found is to rely on cargo doc --open and then use that to browse all of the code interfaces. I’ve started using this instead of just jumping around to code at this point.


Okay, I’m here again seeking for alternatives.

Actually, we already have error chaining in quick-error using into() and context(), and adding traceback manually is easy: here is a full example and a diff to example without tracebacks. The interesting part of example:

quick_error! {
    pub enum Error {
        Io(err: io::Error, path: PathBuf, tb: Backtrace) {
            display("could not read file {:?}: {}\n{:?}", path, err, tb)
            context(path: &'a Path, err: io::Error)
                -> (err, path.to_path_buf(), Backtrace::new())

What’s notable here:

  1. You can just add a var and a Backtrace::new() call. Easy.
  2. You can add backtrace only for some cases, i.e. unexpected errors. Other conditions don’t have to pay for boxing.
  3. The display handler is obviously wrong (includes a traceback), it’s just for showcase here
  4. Traceback objects may be more complicated like Option<Arc<Backtrace>> but in can be type-aliased. And constructor (which checks environment, for example) might be just plain function, no need for a complex macro.

What we want is a pretty printer which walks the causality chain and prints errors with tracebacks. Making a pretty printer is easy as long as we have a trait that exposes all needed information.

Ideally, we would have Error::traceback() in the std::error::Error similar to Error::cause.

If we are out of stdlib we may add a TracebackExt trait:

trait TracebackExt {
  fn traceback(&self) -> Option<&Backtrace>;
  fn cause(&self) -> Option<&Error + TracebackExt>;

It’s easy to get this trait implemented in a way similar to how cause works in quick-error (the only issue is that I don’t wan’t quick-error to depend on backtrace crate)

What do you think?


For those interested, I just converted to using this library (took a while, I had a lot of errors defined!)

A couple of things I noticed:

  1. you will run into: error: recursion limit reached while expanding the macro 'quick_erro', the limit of errors looks to be 10. Edit: it looks like this is inconsistent, 10 in some cases, others allow more, may be some other bug that I’m missing.

  2. I’ve run into an issue where into() isn’t really good enough, especially when using chaining. I decided to just do Error::from(ErrorKind::Kind) where needed.


I published a new build, 0.2.1. The only change is that now error chain lives in its own repo, so the doc links have changed.


I think I’d like to log a message at the point where the error is first noticed (eg, soon after the failed syscall), even if it’s later handled short of terminating the program. I have some boilerplate to do this today, which could potentially be converted to macros. Is this handled by error_chain?

(Apologies if this is in the docs somewhere and I missed it.)


It doesn’t today but it would be easy to add at the same place the backtrace is being generated. Patches welcome!


I’ve just released error-chain 0.2.2. In this release the types { } section may be omitted, and I have added a quick start section to the README.


Contributors: Brian Anderson, Jake Shadle, Nate Mara