Skip to content

n0-computer/n0-error

Repository files navigation

n0-error

Documentation Crates.io

An Rust error library that supports tracking the call-site location of errors.

This crate provides a StackError trait and proc macro to ergonomically work with nested enum and struct errors, while allowing to track the call-site location for the full error chain.

It also has a AnyError type that works similar to anyhow errors while keeping the location metadata of StackError errors accessible through the full error chain.

Example

use n0_error::{e, ensure, Result, StackResultExt, stack_error};

/// The `stack_error` macro controls how to turn our enum into a `StackError`.
///
/// * `add_meta` adds a field to all variants to track the call-site error location
/// * `derive` adds `#[derive(StackError)]`
/// * `from_sources` creates `From` impls for the error sources
#[stack_error(derive, add_meta, from_sources)]
enum MyError {
    /// We can define the error message with the `error` attribute
    #[error("invalid input")]
    InvalidInput { source: InvalidInput },
    /// Or we can define a variant as `transparent`, which forwards the Display impl to the error source.
    #[error(transparent)]
    Io {
        /// For sources that do not implement `StackError`, we have to mark the source as `std_err`.
        #[error(std_err)]
        source: std::io::Error,
    },
}

/// We can use the [`stack_error`] macro on structs as well.
#[stack_error(derive, add_meta)]
#[error("wanted {expected} but got {actual}")]
struct InvalidInput {
    expected: u32,
    actual: u32,
}

fn validate_input(input: u32) -> Result<(), InvalidInput> {
    if input != 23 {
        // With the `e` macro we can construct an error directly without spelling out the `meta` field:
        return Err(e!(InvalidInput { expected: 12, actual: input }))
    }
    /// There's also `bail` and `ensure` macros that expand to include the `meta` field:
    n0_error::ensure!(input == 23, InvalidInput { expected: 23, actual: input });
    Ok(())
}

/// The `Result` type defaults to `AnyError` for the error variant.
///
/// Errors types using the derive macro convert to `AnyError`, and we can add additional context
/// with the result extensions.
fn process(input: u32) -> Result<()> {
    validate_input(input).context("failed to process input")?;
    Ok(())
}

The error returned from process would look like this in the alternate display format:

failed to process input: invalid input: wanted 23 but got 13

and like this in the debug format with RUST_BACKTRACE=1 or RUST_ERROR_LOCATION=1:

failed to process input (examples/basic.rs:61:17)
Caused by:
    invalid input (examples/basic.rs:36:5)
    wanted 23 but got 13 (examples/basic.rs:48:13)

Details

  • All errors using the macro implement the StackError trait, which exposes call-site metadata for where the error occurred. Its source method returns references which may be other stack errors, allowing access to location data for the entire error chain.
  • The proc macro can add a meta field to structs or enum variants. This field stores call-site metadata accessed through the StackError trait.
    • Call-site metadata in the meta field is collected only if RUST_BACKTRACE=1 or RUST_ERROR_LOCATION=1 env variable is set. Otherwise, it is disabled to avoid runtime overhead.
    • The declarative macro e! provides an ergonomic way to construct such errors without explicitly setting the field. The crate's ensure and bail macro also do this.
  • The crate includes an AnyError type, similar to anyhow::Error. When created from a StackError, the call-site metadata is preserved. AnyError is recommended for applications and tests, while libraries should use concrete derived errors.
    • All stack errors convert to AnyError, so they can be propagated to such results with ?.
    • For std errors, use std_context or anyerr to convert to AnyError. For stack errors, use context or simply propagate with ?.
    • Result extension traits provide conversions between results with StackErrors, std::error::Errors to AnyError, with support for attaching context.
  • Both AnyError and all errors using the StackError derive feature consistent, structured output that includes location metadata when available.

Feature flags

  • anyhow (off by default): Enables From<anyhow::Error> for AnyError

License

Copyright 2025 N0, INC.

This project is licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

No description, website, or topics provided.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE
MIT
LICENSE-MIT

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages