Status: Superseded by ADR-280 — its D1-D2 are D28-D29 there, D3 is in D20
Date: September 27, 2026
Target Version: Nikaia 0.0.226
Answers: #199, what the box still costs — an error a
callee raised in a typed channel lost its site when a ? put it in the box
Refines: ADR-157 D2 (the typed envelope, Thrown<E>),
ADR-170 D1 (the list every error carries)
Related: ADR-023 D6 (an error knows where it was raised),
ADR-159 D3 (what the long form says where there is no site),
ADR-239 D3 (a cleanup’s failure joins the error already
leaving)
A function’s failure channel is typed where the compiler can name what it
throws — Thrown<E>, an envelope holding the error, the site of its throw
and the list of failures that joined it — and the box, Box<dyn Error>,
where it cannot. A ? from a typed callee into a boxed caller converted
through the language below’s own From<E: Error> for Box<dyn Error>, because
Thrown<E> was an Error. The envelope went into the box whole, as an opaque
value, and nothing there could reach its site or its list without knowing E:
error.full() said raised below Nikaia; no site recorded about an error the
program itself had thrown, and a failure that joined it later — a cleanup’s,
an overlap branch’s — was dropped.
Thrown<E> put in the box becomes the box’s own envelope, with its site and its list.std writes From<Thrown<E>> for Box<dyn Error>, which makes a Raised of
it: the same error, the same site and trace, and every failure on its list
converted the same way. Thrown<E> is therefore not an Error itself —
were it one, the blanket conversion would still be the one a ? takes, and
the two would overlap. Nothing is lost by it: a typed channel is never a
dyn Error anywhere but on this hop, and this hop is now the envelope’s.
An error that reaches the box with no envelope at all — a library’s — is put in one when something joins it, with no site, so what joins it is kept.
The emitter writes nothing new: the ? is the one it wrote, and the
conversion it takes is std’s.
The settle points of ADR-239 hand a failing cleanup on as a
Thrown<Failure> raised at the function the settle point stands in, so the
long form says raised at both under cleaning up a Flaky failed rather
than no site recorded.
The long form of a boxed error said (no trace; set NIKAIA_TRACE=1 to capture one) under every failure that joined it; the typed envelope said it once, at the top. Both say it once.
?, written by the emitter — .map_err(…) where
the callee’s channel is typed and the caller’s is the box. Eight sites in
the emitter write a ?, each would have to know both channels, and a ninth
would forget. The conversion belongs to the types, where Rust looks for it.