rtb-tui 0.6.4

Reusable TUI widgets: forms, wizards, prompts, tables. Part of the phpboyscout Rust toolkit.
Documentation
---
title: Errors
description: WizardError and RenderError — variants, messages, miette diagnostic codes, and what to do about each.
tags: [reference, errors]
---

# Errors

Two error enums, both `#[non_exhaustive]`, both deriving `Debug`,
`Clone`, `thiserror::Error` and `miette::Diagnostic`.

`#[non_exhaustive]` means a `match` over either one needs a `_` arm
even when it covers every variant listed here. A new variant in a later
release is a minor version bump, not a breaking change, so write the
`_` arm to do something sensible rather than `unreachable!()`.

`Clone` is derived on both so an error can be stored, retried against,
or attached to a telemetry event without being consumed.

## `WizardError`

```rust
#[non_exhaustive]
pub enum WizardError {
    Cancelled,
    Interrupted,
    Step { step: String, message: String },
}
```

| Variant | `Display` output | Diagnostic code | Raised when |
|---|---|---|---|
| `Cancelled` | `wizard cancelled` | `rtb::tui::wizard_cancelled` | The user went back past the first step, by returning `StepOutcome::Back` or by pressing <kbd>Esc</kbd> there |
| `Interrupted` | `wizard interrupted (Ctrl+C)` | `rtb::tui::wizard_interrupted` | Any step returned `InquireError::OperationInterrupted` |
| `Step { step, message }` | ``wizard step `<step>` failed: <message>`` | `rtb::tui::wizard_step` | Any other `InquireError` came out of a step |

`Cancelled` and `Interrupted` are deliberately separate. Both mean the
wizard did not finish, but one is a user deciding not to proceed and
the other is a signal — and a caller that maps them to process exit
codes normally wants different numbers for the two. See
[Map wizard errors to exit codes](../how-to/03-map-wizard-errors-to-exit-codes.md).

### Fields on `WizardError::Step`

- `step: String` — the value `WizardStep::name()` returned for the
  failing step, cloned into an owned `String`.
- `message: String` — the underlying `InquireError`'s `Display` output,
  already stringified.

The original `InquireError` is not retained, so there is no `source()`
chain back to it and no way to match on the original variant. If a
caller needs to branch on the specific `inquire` failure, it has to do
so inside the step, before returning.

## `RenderError`

```rust
#[non_exhaustive]
pub enum RenderError {
    Json(String),
}
```

| Variant | `Display` output | Diagnostic code | Raised when |
|---|---|---|---|
| `Json(String)` | `JSON serialisation failed: <message>` | `rtb::tui::render_json` | `serde_json` rejected the rows passed to `render_json` |

There is no variant for `render_table`, because `render_table` returns
`String` rather than a `Result` — see
[Render helpers](02-render-helpers.md#render_table-output-shape).

The payload is a stringified `serde_json::Error`, not the error itself,
so line and column information is only present if `serde_json` put it
in the message.

## Rendering these errors with miette

Both enums derive `miette::Diagnostic` and carry a `code(...)`, so a
binary that already reports through `miette` gets the code printed with
the message. `miette` is a hard dependency of this crate with its
`fancy` feature enabled — there is no way to opt out of it, and it is
by some distance the largest thing the crate pulls in. See
[Crate metadata](05-crate.md#dependencies).

None of the variants carry a `help(...)`, a `url(...)` or a labelled
source span. What `miette` adds over plain `Display` here is the
diagnostic code and consistent formatting, not a rendered snippet.