Produce user-facing errors¶
RTB treats errors as values: derive them, propagate with ?, and let
the edge report them once. There is no ErrorHandler.check() funnel.
In library crates¶
Every public crate derives both thiserror::Error and
miette::Diagnostic on its error enum:
use miette::Diagnostic;
use thiserror::Error;
#[derive(Debug, Error, Diagnostic)]
pub enum DeployError {
#[error("no target environment configured")]
#[diagnostic(
code(mytool::deploy::no_target),
help("set `deploy.target` in your config or pass --target")
)]
NoTarget,
#[error("upload failed")]
#[diagnostic(code(mytool::deploy::upload))]
Upload(#[source] std::io::Error),
}
Do not use anyhow inside framework crates — it has no diagnostic
surface and ambiguous provenance. (anyhow in tests or examples is
fine.)
In command / application code¶
Return miette::Result<T> and propagate with ?:
async fn run(&self, app: App) -> miette::Result<()> {
let target = resolve_target(&app)?; // DeployError -> miette via ?
Ok(())
}
rtb-cli::Application::run installs miette::set_hook (with the
tool's help channel appended) and a panic hook, so a returned error is
rendered with its code, help, and source chain automatically.
Ad-hoc hints¶
For one-off diagnostics without a dedicated enum variant:
return Err(miette::miette!(
code = "mytool::deploy::locked",
help = "another deploy is in progress; retry shortly",
"deploy lock held by pid {pid}",
));
See rtb-error for the canonical hook pipeline and Engineering Standards §3 for the full error-handling contract.