rtb-error 0.6.4

Error types and the diagnostic report pipeline. Part of the phpboyscout Rust toolkit.
Documentation
---
title: rtb-error
description: Canonical Error enum, Result alias, exit-code attachment and the miette diagnostic-hook pipeline for phpboyscout Rust tools.
---

# `rtb-error`

`rtb-error` is the error-handling foundation of the phpboyscout Rust toolkit.
It gives you four things:

- an umbrella [`Error`]reference/02-error-and-result.md enum for application
  scaffolding failures, and the `Result<T, E = Error>` alias;
- a [`hook`]reference/03-hook.md module that installs a graphical diagnostic
  renderer, a matching panic hook, and a support footer you can change at any
  time;
- [exit-code attachment]reference/04-exit-code.md, so a specific error can
  set a specific process exit code;
- a position on how errors should travel:
  [as values, propagated with `?`, rendered once at the edge]explanation/01-errors-are-values.md.

Everything is built on [`miette`][miette] and [`thiserror`][thiserror]. If you
already use those directly, `rtb-error` adds types and a hook wrapper, not
capability — see
[what it does not do](explanation/03-what-this-does-not-do.md) before adopting
it.

Part of the [phpboyscout Rust toolkit](https://rust.phpboyscout.uk); extracted
from — and still consumed by —
[rust-tool-base](https://gitlab.com/phpboyscout/rust-tool-base).

## Install it

```console
$ cargo add rtb-error miette thiserror
```

`miette` and `thiserror` are separate because you need their derive macros in
your own crate. `rtb-error` re-exports the `Diagnostic` trait and derive, but
not `thiserror::Error`.

## What it looks like

```rust
use std::process::ExitCode;
use rtb_error::exit_code_of;

fn main() -> ExitCode {
    rtb_error::hook::install_with_footer(|| {
        "Report bugs at https://example.invalid/greet/issues".to_string()
    });
    rtb_error::hook::install_panic_hook();

    match run() {
        Ok(()) => ExitCode::SUCCESS,
        Err(report) => {
            eprintln!("Error: {report:?}");
            ExitCode::from(exit_code_of(&report).unwrap_or(1))
        }
    }
}
```

```text
Error: greet::no_name

  x no name given
  help: pass a name: `greet Ada`

Report bugs at https://example.invalid/greet/issues
```

## Where to start

| If you want to… | Go to |
|---|---|
| Build one of these from an empty project | [Tutorial: give your CLI a diagnostic error path]tutorials/01-give-your-cli-a-diagnostic-error-path.md |
| Do one specific thing — install the pipeline, set an exit code, add a footer | [How-to guides]how-to/index.md |
| Look up a variant, a diagnostic code, a default or a failure mode | [Reference]reference/index.md |
| Understand why there is no error-handler object | [Explanation]explanation/index.md |
| Read the generated API docs | [docs.rs/rtb-error]https://docs.rs/rtb-error |

## The one thing to get right

`miette`'s renderer lives in a process-global slot that can only be written
once, and it is filled by whichever comes first: your `install_*` call, or the
construction of the first `miette::Report` anywhere in the process. Install too
late and this crate's handler never runs, the footer never appears, and nothing
warns you.

Put the install calls at the very top of `main`, before any fallible work. The
mechanism is explained in
[Why the hook is installed once](explanation/02-why-the-hook-is-installed-once.md).

## What this does not do

It does not log, does not decide your exit code for you, does not redact
anything, and does not adapt rendering to the terminal the way `miette`'s own
default handler does. There are no Cargo features and no `no_std` build. The
full list, with the workaround for each, is
[What rtb-error does not do](explanation/03-what-this-does-not-do.md).

## Which crates use it

| Crate | How it uses `rtb-error` |
|---|---|
| [rtb-cli]https://docs.rs/rtb-cli `0.9.0` | Installs all three hooks in `Application::run_with_args` when hooks are enabled, sourcing the footer from `ToolMetadata::help`; raises `Error::CommandNotFound` on an unknown subcommand; maps a report to a process exit code with `exit_code_of(report).unwrap_or(1)`; re-exports `Error` and `Result` from its prelude as `RtbError` and `RtbResult` |
| [rtb-app]https://docs.rs/rtb-app `0.9.0` | Declares the dependency and defines the `HelpChannel` that `rtb-cli` turns into the footer text |

`rtb-config`, `rtb-credentials` and `rtb-telemetry` do **not** depend on
`rtb-error` in their currently published versions, despite what older
descriptions of the toolkit said.

None of these crates are required to use `rtb-error`. It has no dependency on
any of them and works in a plain `cargo new` binary — which is what the
[tutorial](tutorials/01-give-your-cli-a-diagnostic-error-path.md) builds.

[miette]: https://crates.io/crates/miette
[thiserror]: https://crates.io/crates/thiserror

## Further reading

The blog carries a curated route through this subject: **[Rust, and what survived the port](https://phpboyscout.uk/topics/rust/)** collects
everything written about it, ordered so you can start at the beginning rather
than newest-first.

!!! tip "Ask phpbotscout"

    ![phpbotscout](https://phpboyscout.uk/images/projects/logo-phpbotscout.png){ width="84" align=left style="border-radius:10px;margin-right:1rem" }

    He answers questions about the projects over on the Discord, citing the docs
    where they already cover it, and offering to raise an issue where they don't.
    Bring a bug, an idea, or a questionable engineering decision.

    [Join the Discord](https://discord.gg/mQzGbmGyzZ){ .md-button .md-button--primary }