error_stack/
lib.rs

1//! A context-aware error library with arbitrary attached user data.
2//!
3//! [![crates.io](https://img.shields.io/crates/v/error-stack)][crates.io]
4//! [![libs.rs](https://img.shields.io/badge/libs.rs-error--stack-orange)][libs.rs]
5//! [![rust-version](https://img.shields.io/static/v1?label=Rust&message=1.83.0/nightly-2025-08-25&color=blue)][rust-version]
6//!
7//! [crates.io]: https://crates.io/crates/error-stack
8//! [libs.rs]: https://lib.rs/crates/error-stack
9//! [rust-version]: https://www.rust-lang.org
10//!
11//! # Overview
12//!
13//! `error-stack` is an error-handling library centered around the idea of building a [`Report`] of
14//! the error as it propagates. A [`Report`] is made up of two concepts:
15//!
16//!   1. Contexts
17//!   2. Attachments
18//!
19//! A context is a view of the world, it helps describe how the current section of code interprets
20//! the error. This is used to capture how various scopes require differing levels of detail and
21//! understanding of the error as it propagates. A [`Report`] always captures the _current context_
22//! in its generic argument.
23//!
24//! As the [`Report`] is built, various pieces of supporting information can be _attached_. These
25//! can be anything that can be shared between threads whether it be a supporting message or a
26//! custom-defined `Suggestion` struct.
27//!
28//! # Quick-Start Guide
29//!
30//! ## In a new project
31//!
32//! ```rust
33//! # #![allow(dead_code)]
34//! use error_stack::{Report, ResultExt};
35//! // using `thiserror` is not neccessary, but convenient
36//! use thiserror::Error;
37//!
38//! // Errors can enumerate variants users care about
39//! // but notably don't need to chain source/inner error manually.
40//! #[derive(Error, Debug)]
41//! enum AppError {
42//!     #[error("serious app error: {consequences}")]
43//!     Serious { consequences: &'static str },
44//!     #[error("trivial app error")]
45//!     Trivial,
46//! }
47//!
48//! type AppResult<T> = Result<T, Report<AppError>>;
49//!
50//! // Errors can also be a plain `struct`, somewhat like in `anyhow`.
51//! #[derive(Error, Debug)]
52//! #[error("logic error")]
53//! struct LogicError;
54//!
55//! type LogicResult<T> = Result<T, Report<LogicError>>;
56//!
57//! fn do_logic() -> LogicResult<()> {
58//!     Ok(())
59//! }
60//!
61//! fn main() -> AppResult<()> {
62//!     // `error-stack` requires developer to properly handle
63//!     // changing error contexts
64//!     do_logic().change_context(AppError::Serious {
65//!         consequences: "math no longer works",
66//!     })?;
67//!
68//!     Ok(())
69//! }
70//! ```
71//!
72//! ## Where to use a Report
73//!
74//! [`Report`] has been designed to be used as the [`Err`] variant of a [`Result`]:
75//!
76//! ```rust
77//! # fn has_permission(_: (), _: ()) -> bool { true }
78//! # fn get_user() -> Result<(), AccessError> { Ok(()) }
79//! # fn get_resource() -> Result<(), AccessError> { Ok(()) }
80//! # #[derive(Debug)] enum AccessError { PermissionDenied((), ()) }
81//! # impl core::fmt::Display for AccessError {
82//! #    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) }
83//! # }
84//! # impl core::error::Error for AccessError {}
85//! use error_stack::{ensure, Report};
86//!
87//! fn main() -> Result<(), Report<AccessError>> {
88//!     let user = get_user()?;
89//!     let resource = get_resource()?;
90//!
91//!     ensure!(
92//!         has_permission(user, resource),
93//!         AccessError::PermissionDenied(user, resource)
94//!     );
95//!
96//!     # const _: &str = stringify! {
97//!     ...
98//!     # }; Ok(())
99//! }
100//! ```
101//!
102//! ### Initializing a Report
103//!
104//! A [`Report`] can be created directly from anything that implements [`Error`] by using
105//! [`Report::new()`], [`IntoReport::into_report()`], or through any of the provided macros
106//! ([`bail!`], [`ensure!`]).
107//!
108//! ```rust
109//! use std::{fs, io, path::Path};
110//!
111//! use error_stack::Report;
112//!
113//! // Note: For demonstration purposes this example does not use `error_stack::Result`.
114//! // As can be seen, it's possible to implicitly convert `io::Error` to `Report<io::Error>`
115//! fn read_file(path: impl AsRef<Path>) -> Result<String, Report<io::Error>> {
116//!     let content = fs::read_to_string(path)?;
117//!
118//!     # const _: &str = stringify! {
119//!     ...
120//!     # }; Ok(content)
121//! }
122//! # let report = read_file("test.txt").unwrap_err();
123//! # assert!(report.contains::<io::Error>());
124//! ```
125//!
126//! ## Using and Expanding the Report
127//!
128//! As mentioned, the library centers around the idea of building a [`Report`] as it propagates.
129//!
130//! ### Changing Context
131//!
132//! The generic parameter in [`Report`] is called the _current context_. When creating a new
133//! [`Report`], the [`Error`] that's provided will be set as the current context. The current
134//! context should encapsulate how the current code interprets the error. As the error propagates,
135//! it will cross boundaries where new information is available, and the previous level of detail is
136//! no longer applicable. These boundaries will often occur when crossing between major modules, or
137//! when execution crosses between crates. At this point the [`Report`] should start to operate in a
138//! new context. To change the context, [`Report::change_context()`] is used:
139//!
140//! (Again, for convenience, using [`ResultExt`] will do that on the [`Err`] variant)
141//!
142//! ```rust
143//! # use std::{fmt, fs, io, path::Path};
144//! use core::error::Error;
145//!
146//! use error_stack::{Report, ResultExt};
147//! # pub type Config = String;
148//!
149//! #[derive(Debug)]
150//! struct ParseConfigError;
151//!
152//! impl fmt::Display for ParseConfigError {
153//!     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
154//!         fmt.write_str("could not parse configuration file")
155//!     }
156//! }
157//!
158//! // It's also possible to implement `Error` instead.
159//! impl Error for ParseConfigError {}
160//!
161//! // For clarification, this example is not using `error_stack::Result`.
162//! fn parse_config(path: impl AsRef<Path>) -> Result<Config, Report<ParseConfigError>> {
163//!     let content = fs::read_to_string(path.as_ref())
164//!         .change_context(ParseConfigError)?;
165//!
166//!     # const _: &str = stringify! {
167//!     ...
168//!     # }; Ok(content)
169//! }
170//! # let report = parse_config("test.txt").unwrap_err();
171//! # assert!(report.contains::<io::Error>());
172//! # assert!(report.contains::<ParseConfigError>());
173//! ```
174//!
175//! ### Building up the Report - Attachments
176//!
177//! Module/crate boundaries are not the only places where information can be embedded within the
178//! [`Report`] however. Additional information can be attached within the current context, whether
179//! this be a string, or any thread-safe object. These attachments are added by using
180//! [`Report::attach_opaque()`] and [`Report::attach()`]:
181//!
182//! ```rust
183//! # // we only test the snapshot on nightly, therefore report is unused (so is render)
184//! # #![cfg_attr(not(nightly), allow(dead_code, unused_variables, unused_imports))]
185//! # use std::{fs, path::Path};
186//! # use error_stack::{Report, ResultExt};
187//! # pub type Config = String;
188//! # #[derive(Debug)] struct ParseConfigError;
189//! # impl ParseConfigError { pub fn new() -> Self { Self } }
190//! # impl std::fmt::Display for ParseConfigError {
191//! #     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192//! #         fmt.write_str("could not parse configuration file")
193//! #     }
194//! # }
195//! # impl core::error::Error for ParseConfigError {}
196//! # #[derive(Debug, PartialEq)]
197//! struct Suggestion(&'static str);
198//!
199//! fn parse_config(path: impl AsRef<Path>) -> Result<Config, Report<ParseConfigError>> {
200//!     let path = path.as_ref();
201//!
202//!     let content = fs::read_to_string(path)
203//!         .change_context(ParseConfigError::new())
204//!         .attach_opaque(Suggestion("use a file you can read next time!"))
205//!         .attach_with(|| format!("could not read file {path:?}"))?;
206//!
207//!     Ok(content)
208//! }
209//! # let report = parse_config("test.txt").unwrap_err();
210//! # assert!(report.contains::<std::io::Error>());
211//! # assert_eq!(report.downcast_ref::<Suggestion>().unwrap(), &Suggestion("use a file you can read next time!"));
212//! # #[cfg(nightly)]
213//! # assert_eq!(report.request_ref::<Suggestion>().next().unwrap(), &Suggestion("use a file you can read next time!"));
214//! # #[cfg(nightly)]
215//! # assert_eq!(report.request_ref::<String>().next().unwrap(), "could not read file \"test.txt\"");
216//! # assert!(report.contains::<ParseConfigError>());
217//! #
218//! # Report::set_color_mode(error_stack::fmt::ColorMode::Emphasis);
219//! # fn render(value: String) -> String {
220//! #     let backtrace = regex::Regex::new(r"backtrace no\. (\d+)\n(?:  .*\n)*  .*").unwrap();
221//! #     let backtrace_info = regex::Regex::new(r"backtrace( with (\d+) frames)? \((\d+)\)").unwrap();
222//! #
223//! #     let value = backtrace.replace_all(&value, "backtrace no. $1\n  [redacted]");
224//! #     let value = backtrace_info.replace_all(value.as_ref(), "backtrace ($3)");
225//! #
226//! #     ansi_to_html::convert(value.as_ref()).unwrap()
227//! # }
228//! #
229//! # #[cfg(nightly)]
230//! # expect_test::expect_file![concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/lib__suggestion.snap")].assert_eq(&render(format!("{report:?}")));
231//! ```
232//!
233//! As seen above, there are ways on attaching more information to the [`Report`]: [`attach`] and
234//! [`attach_opaque`]. These two functions behave similar, but the former has a more restrictive
235//! bound on the attachment: [`Display`] and [`Debug`]. Depending on the function used, printing the
236//! [`Report`] will also use the [`Display`] and [`Debug`] traits to describe the attachment.
237//!
238//! This outputs something like:
239//!
240//! <pre>
241#![cfg_attr(doc, doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/snapshots/doc/lib__suggestion.snap")))]
242//! </pre>
243//!
244//! The `Suggestion` which was added via [`attach`] is not shown directly and only increases the
245//! counter of opaque attachments for the containing [`Error`]. The message which was passed to
246//! [`attach_opaque`], however, is displayed in full. To be able to show attachments that have
247//! been added via [`attach`], one must make use of [hooks](#debug-and-display-hooks) instead.
248//!
249//! [`attach_opaque`]: Report::attach_opaque
250//! [`Display`]: core::fmt::Display
251//! [`Debug`]: core::fmt::Debug
252//!
253//! ### Multiple Errors
254//!
255//! [`Report`] provides native support for combining and propagating multiple errors. This feature
256//! is particularly useful in scenarios such as parallel processing, where multiple errors might
257//! occur independently. In these cases, you can utilize the [`Extend`] trait implementation and the
258//! [`push()`] method to aggregate and propagate all encountered errors, rather than just a single
259//! one.
260//!
261//! error-stack is designed to be explicit about the presence of single or multiple current
262//! contexts. This distinction is reflected in the generic type parameter:
263//!
264//! - [`Report<C>`] indicates that a single current context is present.
265//! - [`Report<[C]>`](Report) signifies that at least one current context is present, with the
266//!   possibility of multiple contexts.
267//!
268//! You can seamlessly convert between these representations using [`Report::expand`] to transform
269//! a single-context report into a multi-context one. Using [`Report::change_context`] will
270//! transform a [`Report<[C]>`](Report) to a [`Report<C2>`], where `C2` is a new context type.
271//!
272//! [`push()`]: Report::push
273//!
274//! ```rust
275//! # use std::{fs, path::Path};
276//! # use error_stack::Report;
277//! # pub type Config = String;
278//!
279//! fn parse_configs(paths: &[impl AsRef<Path>]) -> Result<Vec<Config>, Report<[std::io::Error]>> {
280//!     let mut configs = Vec::new();
281//!     let mut error: Option<Report<[std::io::Error]>> = None;
282//!
283//!     for path in paths {
284//!         let path = path.as_ref();
285//!
286//!         match fs::read_to_string(path) {
287//!             Ok(ok) => {
288//!                 configs.push(ok);
289//!             }
290//!             Err(err) => {
291//!                 if let Some(error) = error.as_mut() {
292//!                     error.push(Report::from(err));
293//!                 } else {
294//!                     error = Some(Report::from(err).expand());
295//!                 }
296//!             }
297//!         }
298//!     }
299//!
300//!     if let Some(error) = error {
301//!         return Err(error);
302//!     }
303//!
304//!     Ok(configs)
305//! }
306//!
307//! # let report = parse_configs(&["test.txt", "test2.txt", "test3.txt"]).unwrap_err();
308//! # assert!(report.contains::<std::io::Error>());
309//! ```
310//!
311//! # In-Depth Explanation
312//!
313//! ## Crate Philosophy
314//!
315//! This crate adds some development overhead in comparison to other error handling strategies,
316//! especially around creating custom root-errors (specifically `error-stack` does not allow using
317//! string-like types). The intention is that this reduces overhead at other parts of the process,
318//! whether that be implementing error-handling, debugging, or observability. The idea that
319//! underpins this is that errors should happen in well-scoped environments like reading a file or
320//! parsing a string into an integer. For these errors, a well-defined error type should be used
321//! (i.e. `io::Error` or `ParseIntError`) instead of creating an error from a string. Requiring a
322//! well-defined type forces users to be conscious about how they classify and group their
323//! **custom** error types, which improves their usability in error-_handling_.
324//!
325//! ### Improving `Result::Err` Types
326//!
327//! By capturing the current [`Error`] in the type parameter, return types in function signatures
328//! continue to explicitly capture the perspective of the current code. This means that **more often
329//! than not** the user is _forced_ to re-describe the error when entering a substantially different
330//! part of the code because the constraints of typed return types will require it. This will happen
331//! most often when crossing module/crate boundaries.
332//!
333//! An example of this is a `ConfigParseError` when produced when parsing a configuration file at
334//! a high-level in the code vs. the lower-level `io::Error` that occurs when reading the file from
335//! disk. The `io::Error` may no longer be valuable at the level of the code that's handling parsing
336//! a config, and re-framing the error in a new type allows the user to incorporate contextual
337//! information that's only available higher-up in the stack.
338//!
339//! ### Compatibility with other Libraries
340//!
341//! `error-stack` uses the standard [`Error`] type which makes it compatible with almost all other
342//! libraries that use that trait.
343//!
344//! This has the added benefit that migrating from other error libraries can often be incremental,
345//! as a lot of popular error library types will work within the [`Report`] struct.
346//!
347//! In addition, `error-stack` supports converting errors generated from the [`anyhow`] or [`eyre`]
348//! crate via [`IntoReportCompat`].
349//!
350//! ### Doing more
351//!
352//! Beyond making new [`Error`] types, the library supports the attachment of arbitrary
353//! thread-safe data. These attachments (and data that is [`provide`]d by the [`Error`] can be
354//! requested through [`Report::request_ref()`]. This gives a novel way to expand standard
355//! error-handling approaches, without decreasing the ergonomics of creating the actual error
356//! variants:
357//!
358//! ```rust
359//! # #![cfg_attr(not(nightly), allow(unused_variables, dead_code))]
360//! # struct Suggestion(&'static str);
361//! # fn parse_config(_: &str) -> Result<(), error_stack::Report<std::io::Error>> { Ok(()) }
362//! fn main() {
363//!     if let Err(report) = parse_config("config.json") {
364//!         # #[cfg(nightly)]
365//!         for suggestion in report.request_ref::<Suggestion>() {
366//!             eprintln!("suggestion: {}", suggestion.0);
367//!         }
368//!     }
369//! }
370//! ```
371//!
372//! [`provide`]: Context::provide
373//!
374//! ## Additional Features
375//!
376//! The above examples will probably cover 90% of the common use case. This crate does have
377//! additional features for more specific scenarios:
378//!
379//! ### Automatic Backtraces
380//!
381//! [`Report`] will try to capture a [`Backtrace`] if `RUST_BACKTRACE` or `RUST_BACKTRACE_LIB` is
382//! set and the `backtrace` feature is enabled (by default this is the case). If on a nightly
383//! toolchain, it will use the [`Backtrace`] if provided by the base [`Error`], and will try to
384//! capture one otherwise.
385//!
386//! Unlike some other approaches, this does not require the user modifying their custom error types
387//! to be aware of backtraces, and doesn't require manual implementations to forward calls down any
388//! wrapped errors.
389//!
390//! ### No-Std compatible
391//!
392//! The complete crate is written for `no-std` environments, which can be used by setting
393//! `default-features = false` in _Cargo.toml_.
394//!
395//! ### Provider API
396//!
397//! This crate uses the [`Provider` API] to provide arbitrary data. This can be done either by
398//! [`attach`]ing them to a [`Report`] or by providing it directly when implementing [`Error`].
399//!
400//! To request a provided type, [`Report::request_ref`] or [`Report::request_value`] are used. Both
401//! return an iterator of all provided values with the specified type. The value, which was provided
402//! most recently will be returned first.
403//!
404//! [`attach`]: Report::attach
405//! [`Provider` API]: https://rust-lang.github.io/rfcs/3192-dyno.html
406//!
407//! ### Macros for Convenience
408//!
409//! Two macros are provided to simplify the generation of a [`Report`].
410//!
411//! - [`bail!`] acts like calling [`IntoReport::into_report()`] but also immediately returns the
412//!   [`Report`] as [`Err`] variant.
413//! - [`ensure!`] will check an expression and if it's evaluated to `false`, it will act like
414//!   [`bail!`].
415//!
416//! ### Span Traces
417//!
418//! The crate comes with built-in support for `tracing`s [`SpanTrace`]. If the `spantrace` feature
419//! is enabled and an [`ErrorLayer`] is set, a [`SpanTrace`] is either used when provided by the
420//! root [`Error`] or will be captured when creating the [`Report`].
421//!
422//! [`ErrorLayer`]: tracing_error::ErrorLayer
423//!
424//! ### Debug Hooks
425//!
426//! One can provide hooks for types added as attachments when the `std` feature is enabled. These
427//! hooks are then used while formatting [`Report`]. This functionality is also used internally by
428//! `error-stack` to render [`Backtrace`], and [`SpanTrace`], which means overwriting and
429//! customizing them is as easy as providing another hook.
430//!
431//! You can add new hooks with [`Report::install_debug_hook`]. Refer to the module-level
432//! documentation of [`fmt`] for further information.
433//!
434//! ### Additional Adaptors
435//!
436//! [`ResultExt`] is a convenient wrapper around `Result<_, impl Error>` and `Result<_, Report<impl
437//! Error>`. It offers [`attach`](ResultExt::attach) and
438//! [`change_context`](ResultExt::change_context) on the [`Result`] directly, but also a lazy
439//! variant that receives a function which is only called if an error happens.
440//!
441//! In addition to [`ResultExt`], this crate also comes with [`FutureExt`], which provides the same
442//! functionality for [`Future`]s.
443//!
444//! [`Future`]: core::future::Future
445//!
446//! ### Colored output and charset selection
447//!
448//! You can override the color support by using the [`Report::set_color_mode`]. To override the
449//! charset used, you can use [`Report::set_charset`]. The default color mode is emphasis.
450//! The default charset is `UTF-8`.
451//!
452//! To automatically detect support if your target output supports unicode and colors you can check
453//! out the `detect.rs` example.
454//!
455//! ### Feature Flags
456//!
457//!  Feature       | Description                                                         | default
458//! ---------------|---------------------------------------------------------------------|----------
459//! `std`          | Enables support for [`Error`]                                       | enabled
460//! `backtrace`    | Enables automatic capturing of [`Backtrace`]s (requires Rust 1.65+) | enabled
461//! `spantrace`    | Enables automatic capturing of [`SpanTrace`]s                       | disabled
462//! `hooks`        | Enables hooks on `no-std` platforms using spin locks                | disabled
463//! `serde`        | Enables serialization support for [`Report`]                        | disabled
464//! `anyhow`       | Provides `into_report` to convert [`anyhow::Error`] to [`Report`]   | disabled
465//! `eyre`         | Provides `into_report` to convert [`eyre::Report`] to [`Report`]    | disabled
466//! `futures`      | Enables support for [`Stream`], requires `unstable`                 | disabled
467//! `unstable`     | Enables unstable features, these features are not covered by semver | disabled
468//!
469//!
470//! [`Error`]: core::error::Error
471//! [`Error::provide`]: core::error::Error::provide
472//! [`Backtrace`]: std::backtrace::Backtrace
473//! [`Display`]: core::fmt::Display
474//! [`Debug`]: core::fmt::Debug
475//! [`SpanTrace`]: tracing_error::SpanTrace
476//! [`Stream`]: futures_core::Stream
477#![cfg_attr(not(feature = "std"), no_std)]
478#![cfg_attr(nightly, feature(error_generic_member_access))]
479#![cfg_attr(all(nightly, feature = "unstable"), feature(try_trait_v2))]
480#![cfg_attr(all(doc, nightly), feature(doc_auto_cfg, doc_cfg))]
481#![cfg_attr(all(nightly, feature = "std"), feature(backtrace_frames))]
482#![cfg_attr(
483    not(miri),
484    doc(test(attr(
485        deny(warnings, clippy::pedantic, clippy::nursery),
486        allow(exported_private_dependencies)
487    )))
488)]
489#![allow(unsafe_code)]
490// This is an error handling library producing Results, not Errors
491#![allow(clippy::missing_errors_doc)]
492
493extern crate alloc;
494
495pub mod future;
496pub mod iter;
497
498mod compat;
499mod frame;
500mod macros;
501mod report;
502mod result;
503
504mod context;
505mod error;
506#[cfg(feature = "unstable")]
507pub mod ext;
508pub mod fmt;
509#[cfg(any(feature = "std", feature = "hooks"))]
510mod hook;
511#[cfg(feature = "serde")]
512mod serde;
513#[cfg(feature = "unstable")]
514mod sink;
515
516#[expect(deprecated, reason = "`core::error::Error` is stable now")]
517pub use self::context::Context;
518#[cfg(all(feature = "unstable", feature = "futures"))]
519pub use self::ext::stream::TryReportStreamExt;
520#[cfg(feature = "unstable")]
521pub use self::ext::{iter::TryReportIteratorExt, tuple::TryReportTupleExt};
522#[expect(deprecated, reason = "We are moving to a more explicit API")]
523pub use self::result::Result;
524#[cfg(feature = "unstable")]
525pub use self::sink::ReportSink;
526pub use self::{
527    compat::IntoReportCompat,
528    context::{Attachment, OpaqueAttachment},
529    frame::{AttachmentKind, Frame, FrameKind},
530    report::{IntoReport, Report},
531};
532#[doc(inline)]
533pub use self::{future::FutureExt, result::ResultExt};
534
535#[cfg(test)]
536#[expect(dead_code)]
537mod tests {
538
539    use core::mem;
540
541    use crate::Report;
542
543    const fn assert_send<T: Send>() {}
544
545    const fn assert_sync<T: Sync>() {}
546
547    const fn assert_static<T: 'static>() {}
548
549    const fn report() {
550        assert_send::<Report<()>>();
551        assert_sync::<Report<()>>();
552        assert_static::<Report<()>>();
553    }
554
555    #[test]
556    fn size() {
557        assert_eq!(mem::size_of::<Report<()>>(), mem::size_of::<*const ()>());
558    }
559}