#[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().

Backtrace and SpanTrace

Report is able to provide a Backtrace and a SpanTrace, which can be retrieved by calling backtrace() or span_trace() 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.

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)
    .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()).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)))?;

    ...
}

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/demo.rs (line 48)
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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 experiment = parse_experiment(description)
                .attach_printable(format!("Experiment {exp_id} could not be parsed"))
                .change_context(ExperimentError)?;

            Ok(move || experiment.0 * experiment.1)
        })
        .collect::<Result<Vec<_>, ExperimentError>>()
        .attach_printable("Unable to setup 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.

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")
    .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/demo.rs (line 49)
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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 experiment = parse_experiment(description)
                .attach_printable(format!("Experiment {exp_id} could not be parsed"))
                .change_context(ExperimentError)?;

            Ok(move || experiment.0 * experiment.1)
        })
        .collect::<Result<Vec<_>, ExperimentError>>()
        .attach_printable("Unable to setup 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.

Available on nightly and crate feature std only.

Returns the backtrace of the error, if captured.

Note, that RUST_BACKTRACE or RUST_LIB_BACKTRACE has to be set to enable backtraces.

Available on crate feature spantrace only.

Returns the span trace of the error, if captured.

Note, that ErrorLayer has to be enabled to enable span traces.

Returns an iterator over the Frame stack of the report.

Examples found in repository?
examples/json_output.rs (line 61)
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
103
104
105
106
fn main() -> Result<(), MapError> {
    // This hook will be executed instead of the default implementation when `Debug` is called
    Report::set_debug_hook(|report, fmt| {
        #[derive(Serialize)]
        struct Context {
            context: String,
            attachments: Vec<String>,
        }

        let mut output = Vec::new();
        let mut attachments = Vec::new();

        for frame in report.frames() {
            match frame.kind() {
                FrameKind::Context(context) => {
                    output.push(Context {
                        context: context.to_string(),
                        attachments: attachments.clone(),
                    });
                    attachments.clear();
                }
                FrameKind::Attachment(AttachmentKind::Printable(attachment)) => {
                    attachments.push(attachment.to_string());
                }
                FrameKind::Attachment(_) => {}
            }
        }

        if fmt.alternate() {
            fmt.write_str(&serde_json::to_string_pretty(&output).expect("Could not format report"))
        } else {
            fmt.write_str(&serde_json::to_string(&output).expect("Could not format report"))
        }
    })
    .expect("Hook was set twice");

    let mut config = HashMap::default();

    // Create an entry with "foo" as key
    create_new_entry(&mut config, "foo", 1).attach_printable("Could not create new entry")?;

    // Purposefully cause an error by attempting to create another entry with "foo" as key
    create_new_entry(&mut config, "foo", 2).attach_printable("Could not create new entry")?;

    // Will output something like
    // ```json
    // [
    //   {
    //     "context": "Entry \"foo\" is already occupied by 1",
    //     "attachments": [
    //       "Could not create new entry"
    //     ]
    //   }
    // ]
    // ```

    Ok(())
}

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 hooks 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

Errors
  • Returns an error if a debug hook was already set
Example
use std::io::{Error, ErrorKind};

use error_stack::{report, Report};

Report::set_debug_hook(|_, fmt| write!(fmt, "custom debug implementation"))?;

let report = report!(Error::from(ErrorKind::InvalidInput));
assert_eq!(format!("{report:?}"), "custom debug implementation");
Examples found in repository?
examples/json_output.rs (lines 51-82)
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
103
104
105
106
fn main() -> Result<(), MapError> {
    // This hook will be executed instead of the default implementation when `Debug` is called
    Report::set_debug_hook(|report, fmt| {
        #[derive(Serialize)]
        struct Context {
            context: String,
            attachments: Vec<String>,
        }

        let mut output = Vec::new();
        let mut attachments = Vec::new();

        for frame in report.frames() {
            match frame.kind() {
                FrameKind::Context(context) => {
                    output.push(Context {
                        context: context.to_string(),
                        attachments: attachments.clone(),
                    });
                    attachments.clear();
                }
                FrameKind::Attachment(AttachmentKind::Printable(attachment)) => {
                    attachments.push(attachment.to_string());
                }
                FrameKind::Attachment(_) => {}
            }
        }

        if fmt.alternate() {
            fmt.write_str(&serde_json::to_string_pretty(&output).expect("Could not format report"))
        } else {
            fmt.write_str(&serde_json::to_string(&output).expect("Could not format report"))
        }
    })
    .expect("Hook was set twice");

    let mut config = HashMap::default();

    // Create an entry with "foo" as key
    create_new_entry(&mut config, "foo", 1).attach_printable("Could not create new entry")?;

    // Purposefully cause an error by attempting to create another entry with "foo" as key
    create_new_entry(&mut config, "foo", 2).attach_printable("Could not create new entry")?;

    // Will output something like
    // ```json
    // [
    //   {
    //     "context": "Entry \"foo\" is already occupied by 1",
    //     "attachments": [
    //       "Could not create new entry"
    //     ]
    //   }
    // ]
    // ```

    Ok(())
}
Available on crate feature hooks 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.

Errors
  • Returns an error if a display hook was already set
Example
use std::io::{Error, ErrorKind};

use error_stack::{report, Report};

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

Converts to this type from the input type.

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.

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