Why the hook is installed once and the footer is not¶
The constraint: miette's hook can only be set once¶
miette stores its error hook in a static OnceLock. miette::set_hook
succeeds on the first call and returns InstallError on every call after it.
That is a reasonable design for a global: it makes rendering behaviour
predictable, because nothing can change it out from under a running program.
It is also awkward for a framework. A tool's support footer — "report bugs at
…" — is not known when main starts. It comes from configuration, or from
metadata that is assembled a few frames later. If the footer had to be baked
into the handler at install time, the framework would have to defer installing
until it knew the footer, which is exactly the window in which an early error
could be raised and rendered without one.
The resolution: an indirection the handler owns¶
So rtb-error installs a handler that does not hold the footer. It holds a
reference to a slot:
The handler delegates the structural render to miette's
GraphicalReportHandler, then reads that slot at render time and appends
whatever it finds. miette's global is written once, as miette intends. The
footer is written as often as you like, because it lives in a slot this crate
owns.
The visible result is that all three install_* functions can be called in any
order, any number of times, and the most recent footer is the one that appears.
That property is the whole reason the wrapper exists — without it, the API
would have to be "install once, and get it right".
Why installing early matters more than it looks¶
The same OnceLock that makes the wrapper necessary also creates the crate's
sharpest edge, and it is worth understanding rather than memorising.
miette does not install its default handler eagerly. It installs it lazily,
the first time a Report is constructed, via HOOK.get_or_init(…). So the
slot is not filled at program start; it is filled by whichever comes first —
your install call, or the first error anywhere in the process.
Lose that race and everything downstream is silent. install_report_handler
still returns (). install_with_footer still stores the closure. The closure
is simply never called, because the handler that would have called it was never
installed. There is no error, no warning, and no way to interrogate the state
afterwards — miette exposes no "is a hook installed" predicate.
This is a genuine trade. A fallible install_report_handler() -> Result<(), _>
would surface the problem, but it would also force every caller to handle a
failure they cannot do anything about, and it would make the idempotent
"call it twice, it's fine" contract impossible to state. The crate chose the
quiet API and the strict ordering rule. The rule is: install first, before any
fallible work.
Why the footer closure is caught rather than trusted¶
The footer is a caller-supplied closure that runs deep inside a render, while a read lock is held, potentially while a panic is already being reported. Two things go wrong if it panics and nothing guards it.
The panic propagates out of a Display-style path where callers are not
expecting one. And if miette's panic hook is installed, that hook renders the
panic through this same handler, which calls the same panicking closure again —
a double panic, which aborts the process.
So the handler wraps the call in catch_unwind, releases the read guard before
writing anything to the formatter, and sets a thread-local re-entry flag that
makes a nested render skip the footer step entirely. Those three measures are
the toolkit's engineering standard for any framework-global handler that calls
user code — rust-tool-base engineering standards
§1.3
— and this handler is the reference implementation named there.
The cost of that safety is silence: a footer closure that panics produces no footer and no complaint from this crate. Keep the closure trivial — format a string that is already in hand, do not compute anything that can fail.
Why the handler is GraphicalReportHandler and not miette's default¶
miette's default hook builds a MietteHandler, which inspects the
environment: terminal width, hyperlink support, NO_GRAPHICS for the
narratable renderer. rtb-error installs a GraphicalReportHandler directly,
because the footer has to be appended around a handler whose rendering entry
point this crate can call, and GraphicalReportHandler::render_report is that
entry point.
The consequence is that the environment detection MietteHandler performs is
lost. Width becomes a fixed 200 columns, NO_GRAPHICS stops switching to the
narratable renderer, and hyperlinks are always emitted. Those differences are
tabulated in the hook reference;
they are a side effect of the wrapping, not a decision anybody made about how
diagnostics should look.