#[repr(transparent)]
pub struct Report<C> { /* private fields */ }
Expand description

Contains a Frame stack consisting of Contexts and attachments.

Attachments can be added by using attach(). The Frame stack can be iterated by using frames().

When creating a Report by using new(), the passed Context is used to set the current context on the Report. To provide a new one, use change_context().

Attachments, and objects provided by a Context, are directly retrievable by calling request_ref() or request_value().

Multiple Errors

Report is able to represent multiple errors that have occurred. Errors can be combined using the extend_one(), which will add the Frame stack of the other error as an additional source to the current report.

Backtrace and SpanTrace

Report is able to provide a Backtrace and a SpanTrace, which can be retrieved by calling request_ref::<Backtrace>() or request_ref::<SpanTrace>() (downcast_ref::<SpanTrace>() on stable) respectively. If the root context provides a Backtrace or a SpanTrace, those are returned, otherwise, if configured, an attempt is made to capture them when creating a Report. To enable capturing of the backtrace, make sure RUST_BACKTRACE or RUST_LIB_BACKTRACE is set according to the Backtrace documentation. To enable capturing of the span trace, an ErrorLayer has to be enabled. Please also see the Feature Flags section. A single Report can have multiple Backtraces and SpanTraces, depending on the amount of related errors the Report consists of. Therefore it isn’t guaranteed that request_ref() will only ever return a single Backtrace or SpanTrace.

Examples

Provide a context for an error:

use error_stack::{IntoReport, ResultExt, Result};

let config_path = "./path/to/config.file";
let content = std::fs::read_to_string(config_path)
    .into_report()
    .attach_printable_lazy(|| format!("failed to read config file {config_path:?}"))?;

...

Enforce a context for an error:

use std::{fmt, path::{Path, PathBuf}};

use error_stack::{Context, IntoReport, Report, ResultExt};

#[derive(Debug)]
enum RuntimeError {
    InvalidConfig(PathBuf),
    ...
}

#[derive(Debug)]
enum ConfigError {
    IoError,
    ...
}

impl fmt::Display for RuntimeError {
    ...
}
impl fmt::Display for ConfigError {
    ...
}

impl Context for RuntimeError {}
impl Context for ConfigError {}

fn read_config(path: impl AsRef<Path>) -> Result<String, Report<ConfigError>> {
    std::fs::read_to_string(path.as_ref()).into_report().change_context(ConfigError::IoError)
}

fn main() -> Result<(), Report<RuntimeError>> {
    let config_path = "./path/to/config.file";
    let config = read_config(config_path)
            .change_context_lazy(|| RuntimeError::InvalidConfig(PathBuf::from(config_path)))?;

    ...
}

Get the attached Backtrace and SpanTrace:

use error_stack::{IntoReport, ResultExt, Result};

let config_path = "./path/to/config.file";
let content = std::fs::read_to_string(config_path)
    .into_report()
    .attach_printable_lazy(|| format!("failed to read config file {config_path:?}"));

let content = match content {
    Err(err) => {
        for backtrace in err.request_ref::<std::backtrace::Backtrace>() {
            println!("backtrace: {backtrace}");
        }

        for span_trace in err.request_ref::<tracing_error::SpanTrace>() {
            println!("span trace: {span_trace}")
        }

        return Err(err)
    }

    Ok(ok) => ok
};

...

Implementations

Creates a new Report<Context> from a provided scope.

If context does not provide Backtrace/SpanTrace then this attempts to capture them directly. Please see the Backtrace and SpanTrace section of the Report documentation for more information.

Examples found in repository?
examples/exit_code.rs (line 19)
18
19
20
21
22
23
24
fn main() -> ExitCode {
    let report = Report::new(CustomError)
        .attach(ExitCode::from(100))
        .attach_printable("this error has an exit code of 100!");

    report.report()
}
More examples
Hide additional examples
examples/demo.rs (line 67)
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
fn start_experiments(
    experiment_ids: &[usize],
    experiment_descriptions: &[&str],
) -> Result<Vec<u64>, ExperimentError> {
    let experiments = experiment_ids
        .iter()
        .map(|exp_id| {
            let description = experiment_descriptions.get(*exp_id).ok_or_else(|| {
                Report::new(ExperimentError)
                    .attach_printable(format!("experiment {exp_id} has no valid description"))
            })?;

            let experiments = parse_experiment(description)
                .attach_printable(format!("experiment {exp_id} could not be parsed"))
                .change_context(ExperimentError)?;

            let experiments = experiments
                .into_iter()
                .map(|(a, b)| move || a * b)
                .collect::<Vec<_>>();

            Ok(experiments)
        })
        .fold(
            Ok(vec![]),
            |accum: Result<_, ExperimentError>, value| match (accum, value) {
                (Ok(mut accum), Ok(value)) => {
                    accum.extend(value);

                    Ok(accum)
                }
                (Ok(_), Err(err)) => Err(err),
                (Err(accum), Ok(_)) => Err(accum),
                (Err(mut accum), Err(err)) => {
                    accum.extend_one(err);

                    Err(accum)
                }
            },
        )
        .attach_printable("unable to set up experiments")?;

    Ok(experiments.iter().map(|experiment| experiment()).collect())
}
👎Deprecated since 0.2.0: a report might contain multiple backtraces, use request_ref::<Backtrace>() instead
Available on nightly and crate feature std only.
👎Deprecated since 0.2.0: a report might contain multiple spantraces, use request_ref::<SpanTrace>() instead
Available on crate feature spantrace only.

Merge two Reports together

This function appends the current_frames() of the other Report to the current_frames() of this report. Meaning A.extend_one(B) -> A.current_frames() = A.current_frames() + B.current_frames()

use std::{
    fmt::{Display, Formatter},
    path::Path,
};

use error_stack::{Context, Report, IntoReport, ResultExt};

#[derive(Debug)]
struct IoError;

impl Display for IoError {
            ...
}


fn read_config(path: impl AsRef<Path>) -> Result<String, Report<IoError>> {
    std::fs::read_to_string(path.as_ref())
        .into_report()
        .change_context(IoError)
}

let mut error1 = read_config("config.txt").unwrap_err();
let error2 = read_config("config2.txt").unwrap_err();
let mut error3 = read_config("config3.txt").unwrap_err();

error1.extend_one(error2);
error3.extend_one(error1);

// ^ This is equivalent to:
// error3.extend_one(error1);
// error3.extend_one(error2);

This function implements the same functionality as Extend::extend_one (#7261). Once stabilised this function will be removed in favor of Extend.

Examples found in repository?
examples/demo.rs (line 38)
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
fn parse_experiment(description: &str) -> Result<Vec<(u64, u64)>, ParseExperimentError> {
    let values = description
        .split(' ')
        .map(|value| {
            value
                .parse::<u64>()
                .into_report()
                .attach_printable_lazy(|| format!("{value:?} could not be parsed as experiment"))
        })
        .map(|value| value.map(|ok| (ok, 2 * ok)))
        .fold(Ok(vec![]), |accum, value| match (accum, value) {
            (Ok(mut accum), Ok(value)) => {
                accum.push(value);

                Ok(accum)
            }
            (Ok(_), Err(err)) => Err(err),
            (Err(accum), Ok(_)) => Err(accum),
            (Err(mut accum), Err(err)) => {
                accum.extend_one(err);

                Err(accum)
            }
        })
        .change_context(ParseExperimentError)?;

    Ok(values)
}

#[derive(Debug)]
struct ExperimentError;

impl fmt::Display for ExperimentError {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str("experiment error: could not run experiment")
    }
}

impl Context for ExperimentError {}

fn start_experiments(
    experiment_ids: &[usize],
    experiment_descriptions: &[&str],
) -> Result<Vec<u64>, ExperimentError> {
    let experiments = experiment_ids
        .iter()
        .map(|exp_id| {
            let description = experiment_descriptions.get(*exp_id).ok_or_else(|| {
                Report::new(ExperimentError)
                    .attach_printable(format!("experiment {exp_id} has no valid description"))
            })?;

            let experiments = parse_experiment(description)
                .attach_printable(format!("experiment {exp_id} could not be parsed"))
                .change_context(ExperimentError)?;

            let experiments = experiments
                .into_iter()
                .map(|(a, b)| move || a * b)
                .collect::<Vec<_>>();

            Ok(experiments)
        })
        .fold(
            Ok(vec![]),
            |accum: Result<_, ExperimentError>, value| match (accum, value) {
                (Ok(mut accum), Ok(value)) => {
                    accum.extend(value);

                    Ok(accum)
                }
                (Ok(_), Err(err)) => Err(err),
                (Err(accum), Ok(_)) => Err(accum),
                (Err(mut accum), Err(err)) => {
                    accum.extend_one(err);

                    Err(accum)
                }
            },
        )
        .attach_printable("unable to set up experiments")?;

    Ok(experiments.iter().map(|experiment| experiment()).collect())
}

Adds additional information to the Frame stack.

This behaves like attach_printable() but will not be shown when printing the Report. To benefit from seeing attachments in normal error outputs, use attach_printable()

Note: attach_printable() will be deprecated when specialization is stabilized and it becomes possible to merge these two methods.

Examples found in repository?
examples/exit_code.rs (line 20)
18
19
20
21
22
23
24
fn main() -> ExitCode {
    let report = Report::new(CustomError)
        .attach(ExitCode::from(100))
        .attach_printable("this error has an exit code of 100!");

    report.report()
}

Adds additional (printable) information to the Frame stack.

This behaves like attach() but the display implementation will be called when printing the Report.

Note: This will be deprecated in favor of attach() when specialization is stabilized it becomes possible to merge these two methods.

Example
use std::{fmt, fs};

use error_stack::{IntoReport, ResultExt};

#[derive(Debug)]
pub struct Suggestion(&'static str);

impl fmt::Display for Suggestion {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt.write_str(self.0)
    }
}

let error = fs::read_to_string("config.txt")
    .into_report()
    .attach(Suggestion("better use a file which exists next time!"));
let report = error.unwrap_err();
let suggestion = report.request_ref::<Suggestion>().next().unwrap();

assert_eq!(suggestion.0, "better use a file which exists next time!");
Examples found in repository?
examples/exit_code.rs (line 21)
18
19
20
21
22
23
24
fn main() -> ExitCode {
    let report = Report::new(CustomError)
        .attach(ExitCode::from(100))
        .attach_printable("this error has an exit code of 100!");

    report.report()
}
More examples
Hide additional examples
examples/demo.rs (line 68)
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
fn start_experiments(
    experiment_ids: &[usize],
    experiment_descriptions: &[&str],
) -> Result<Vec<u64>, ExperimentError> {
    let experiments = experiment_ids
        .iter()
        .map(|exp_id| {
            let description = experiment_descriptions.get(*exp_id).ok_or_else(|| {
                Report::new(ExperimentError)
                    .attach_printable(format!("experiment {exp_id} has no valid description"))
            })?;

            let experiments = parse_experiment(description)
                .attach_printable(format!("experiment {exp_id} could not be parsed"))
                .change_context(ExperimentError)?;

            let experiments = experiments
                .into_iter()
                .map(|(a, b)| move || a * b)
                .collect::<Vec<_>>();

            Ok(experiments)
        })
        .fold(
            Ok(vec![]),
            |accum: Result<_, ExperimentError>, value| match (accum, value) {
                (Ok(mut accum), Ok(value)) => {
                    accum.extend(value);

                    Ok(accum)
                }
                (Ok(_), Err(err)) => Err(err),
                (Err(accum), Ok(_)) => Err(accum),
                (Err(mut accum), Err(err)) => {
                    accum.extend_one(err);

                    Err(accum)
                }
            },
        )
        .attach_printable("unable to set up experiments")?;

    Ok(experiments.iter().map(|experiment| experiment()).collect())
}

Add a new Context object to the top of the Frame stack, changing the type of the Report.

Please see the Context documentation for more information.

Return the direct current frames of this report, to get an iterator over the topological sorting of all frames refer to frames()

This is not the same as Report::current_context, this function gets the underlying frames that make up this report, while Report::current_context traverses the stack of frames to find the current context. A Report and be made up of multiple Frames, which stack on top of each other. Considering PrintableA<PrintableA<Context>>, Report::current_frames will return the “outer” layer PrintableA, while Report::current_context will return the underlying Context (the current type parameter of this Report)

Using Extend and extend_one(), a Report can additionally be made up of multiple stacks of frames and builds a “group” of them, but a Report can only ever have a single Context, therefore this function returns a slice instead, while Report::current_context only returns a single reference.

Returns an iterator over the Frame stack of the report.

Returns an iterator over the Frame stack of the report with mutable elements.

Available on nightly only.

Creates an iterator of references of type T that have been attached or that are provided by Context objects.

Examples found in repository?
examples/parse_config.rs (line 45)
41
42
43
44
45
46
47
48
49
fn main() {
    if let Err(report) = parse_config("config.json") {
        eprintln!("{report:?}");
        #[cfg(nightly)]
        for suggestion in report.request_ref::<Suggestion>() {
            eprintln!("Suggestion: {}", suggestion.0);
        }
    }
}
Available on nightly only.

Creates an iterator of values of type T that have been attached or that are provided by Context objects.

Returns if T is the type held by any frame inside of the report.

T could either be an attachment or a Context.

Example
fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
    ...
}

let report = read_file("test.txt").unwrap_err();
assert!(report.contains::<io::Error>());

Searches the frame stack for a context provider T and returns the most recent context found.

T can either be an attachment or a Context.

Example
use std::io;

fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
    ...
}

let report = read_file("test.txt").unwrap_err();
let io_error = report.downcast_ref::<io::Error>().unwrap();
assert_eq!(io_error.kind(), io::ErrorKind::NotFound);

Searches the frame stack for an instance of type T, returning the most recent one found.

T can either be an attachment or a Context.

Returns the current context of the Report.

If the user want to get the latest context, current_context can be called. If the user wants to handle the error, the context can then be used to directly access the context’s type. This is only possible for the latest context as the Report does not have multiple generics as this would either require variadic generics or a workaround like tuple-list.

This is one disadvantage of the library in comparison to plain Errors, as in these cases, all context types are known.

Example
use std::io;

fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
    ...
}

let report = read_file("test.txt").unwrap_err();
let io_error = report.current_context();
assert_eq!(io_error.kind(), io::ErrorKind::NotFound);
Available on crate feature std only.

Can be used to globally set a Debug format hook, for a specific type T.

This hook will be called on every Debug call, if an attachment with the same type has been found.

Examples
use std::io::{Error, ErrorKind};

use error_stack::{
    report, Report,
};

struct Suggestion(&'static str);

Report::install_debug_hook::<Suggestion>(|value, context| {
    context.push_body(format!("suggestion: {}", value.0));
});

let report =
    report!(Error::from(ErrorKind::InvalidInput)).attach(Suggestion("oh no, try again"));

println!("{report:?}");

Which will result in something like:

invalid input parameter
src/hook.rs:19:5
backtrace (1)
suggestion: oh no, try again

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

backtrace no. 1
  [redacted]

This example showcases the ability of hooks to be invoked for values provided via the Provider API using Error::provide.

#![feature(error_generic_member_access, provide_any)]

use std::any::Demand;
use std::error::Error;
use std::fmt::{Display, Formatter};
use error_stack::{Report, report};

struct Suggestion(&'static str);

#[derive(Debug)]
struct ErrorCode(u64);


#[derive(Debug)]
struct UserError {
    code: ErrorCode
}

impl Display for UserError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        f.write_str("invalid user input")
    }
}

impl Error for UserError {
 fn provide<'a>(&'a self, req: &mut Demand<'a>) {
   req.provide_value(Suggestion("try better next time!"));
   req.provide_ref(&self.code);
 }
}

Report::install_debug_hook::<Suggestion>(|Suggestion(value), context| {
    context.push_body(format!("suggestion: {value}"));
});
Report::install_debug_hook::<ErrorCode>(|ErrorCode(value), context| {
    context.push_body(format!("error code: {value}"));
});

let report = report!(UserError {code: ErrorCode(420)});

println!("{report:?}");

Which will result in something like:

invalid user input
src/hook.rs:49:14
suggestion: try better next time!
error code: 420
backtrace (1)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

backtrace no. 1
  [redacted]
👎Deprecated since 0.2.0: use Report::install_debug_hook() instead
Available on crate feature std only.

Globally sets a hook which is called when formatting Report with the Debug trait.

By intercepting the default Debug implementation, this hook adds the possibility for downstream crates to provide their own formatting like colored output or a machine-readable output (i.e. JSON).

If not set, Debug will print

Note

Since 0.2 this will overwrite the previous hook (if set) instead of returning HookAlreadySet.

Errors

No longer returns an error since version 0.2, the return value has been preserved for compatibility.

Example
use std::io::{Error, ErrorKind};

use error_stack::{report, Report};

#[allow(deprecated)]
Report::set_debug_hook(|_, fmt| write!(fmt, "custom debug implementation"))?;

let report = report!(Error::from(ErrorKind::InvalidInput));
assert_eq!(format!("{report:?}"), "custom debug implementation");
👎Deprecated since 0.2.0
Available on crate feature std only.

Globally sets a hook that is called when formatting Report with the Display trait.

By intercepting the default Display implementation, this hook adds the possibility for downstream crates to provide their own formatting like colored output or a machine-readable output (i.e. JSON).

If not set, Display will print the latest error and, if alternate formatting is enabled ("{:#}") and it exists, its direct cause.

Note

Since 0.2 this will overwrite the previous hook (if set) instead of returning HookAlreadySet.

Errors

No longer returns an error since version 0.2, the return value has been preserved for compatibility.

Example
use std::io::{Error, ErrorKind};

use error_stack::{report, Report};

#[allow(deprecated)]
Report::set_display_hook(|_, fmt| write!(fmt, "custom display implementation"))?;

let report = report!(Error::from(ErrorKind::InvalidInput));
assert_eq!(report.to_string(), "custom display implementation");

Trait Implementations

Formats the value using the given formatter. Read more
Formats the value using the given formatter. Read more
Extends a collection with the contents of an iterator. Read more
🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
Converts to this type from the input type.
Creates a value from an iterator. Read more
Is called to get the representation of the value as status code. This status code is returned to the operating system. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more
Converts to this type from the input type.

Returns the argument unchanged.

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Instruments this type with the current Span, returning an Instrumented wrapper. Read more

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Set the foreground color generically Read more
Set the background color generically. Read more
Change the foreground color to black
Change the background color to black
Change the foreground color to red
Change the background color to red
Change the foreground color to green
Change the background color to green
Change the foreground color to yellow
Change the background color to yellow
Change the foreground color to blue
Change the background color to blue
Change the foreground color to magenta
Change the background color to magenta
Change the foreground color to purple
Change the background color to purple
Change the foreground color to cyan
Change the background color to cyan
Change the foreground color to white
Change the background color to white
Change the foreground color to the terminal default
Change the background color to the terminal default
Change the foreground color to bright black
Change the background color to bright black
Change the foreground color to bright red
Change the background color to bright red
Change the foreground color to bright green
Change the background color to bright green
Change the foreground color to bright yellow
Change the background color to bright yellow
Change the foreground color to bright blue
Change the background color to bright blue
Change the foreground color to bright magenta
Change the background color to bright magenta
Change the foreground color to bright purple
Change the background color to bright purple
Change the foreground color to bright cyan
Change the background color to bright cyan
Change the foreground color to bright white
Change the background color to bright white
Make the text bold
Make the text dim
Make the text italicized
Make the text italicized
Make the text blink
Make the text blink (but fast!)
Swap the foreground and background colors
Hide the text
Cross out the text
Set the foreground color at runtime. Only use if you do not know which color will be used at compile-time. If the color is constant, use either OwoColorize::fg or a color-specific method, such as OwoColorize::green, Read more
Set the background color at runtime. Only use if you do not know what color to use at compile-time. If the color is constant, use either OwoColorize::bg or a color-specific method, such as OwoColorize::on_yellow, Read more
Set the foreground color to a specific RGB value.
Set the background color to a specific RGB value.
Sets the foreground color to an RGB value.
Sets the background color to an RGB value.
Apply a runtime-determined style
Available on crate feature supports-colors only.
Apply a given transformation function to all formatters if the given stream supports at least basic ANSI colors, allowing you to conditionally apply given styles/colors. Read more
Converts the given value to a String. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.
Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more