fix(swarm-queue-client): export chain, and use it where anyhow used to
Review catch: `anyhow::Error`'s Display special-cases `f.alternate()` to
walk the source chain; thiserror's derive does not, so `{e:#}` and `{e}`
render identically for the new error type. Every call site that held an
`anyhow::Error`, formatted it with `{:#}`, and now holds this crate's
error kept compiling, kept looking right, and silently dropped the cause.
`chain()` was written for exactly this and then left private, applied only
to the auth callback I happened to be editing. Its own doc comment argues
that dropping the source chain is wrong, which made it the one thing in
the PR that should not have had a scope of one.
The controller's "swarm queue unreachable" warning is the site this fixes
here; the stacked PR fixes the two boot-warning banners, which matter more
still — one-shot, no retry, and they leak until restart.
This commit is contained in:
parent
d71222c206
commit
79bc198165
2 changed files with 21 additions and 7 deletions
|
|
@ -318,7 +318,13 @@ async fn main() -> Result<()> {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tracing::warn!(error = format!("{e:#}"), "swarm queue unreachable");
|
// `chain`, not `{:#}`: this is the queue client's own
|
||||||
|
// error type, and thiserror's Display ignores the
|
||||||
|
// alternate flag — the source would be dropped silently.
|
||||||
|
tracing::warn!(
|
||||||
|
error = swarm_queue_client::chain(&e),
|
||||||
|
"swarm queue unreachable"
|
||||||
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -88,12 +88,20 @@ pub enum Error {
|
||||||
|
|
||||||
/// Render an error and its source chain on one line.
|
/// Render an error and its source chain on one line.
|
||||||
///
|
///
|
||||||
/// The auth callback below hands `async-nats` a *string*, so a `Display`
|
/// **Use this instead of `{:#}` anywhere an [`Error`] is rendered without
|
||||||
/// that stopped at the top message would drop the cause — which is the
|
/// first being `?`-converted into an `anyhow::Error`.** `anyhow`'s
|
||||||
/// half that says why the mint failed. `anyhow`'s `{:#}` did this for
|
/// `Display` special-cases `f.alternate()` to walk the source chain;
|
||||||
/// free; a library owes its callers the same detail without owing them
|
/// `thiserror`'s derive does not, so `{e:#}` and `{e}` render
|
||||||
/// anyhow.
|
/// identically for this type. A call site that formatted an
|
||||||
fn chain(error: &dyn std::error::Error) -> String {
|
/// `anyhow::Error` with `{:#}` and now holds an [`Error`] therefore keeps
|
||||||
|
/// compiling, keeps looking right, and silently drops the cause — which
|
||||||
|
/// is the half that says *why*, and is exactly what a one-shot boot
|
||||||
|
/// warning with no retry needs most.
|
||||||
|
///
|
||||||
|
/// Public for that reason: the fix cannot live only inside this crate's
|
||||||
|
/// own auth callback while the callers it was written for reach for
|
||||||
|
/// `{:#}` and get nothing.
|
||||||
|
pub fn chain(error: &dyn std::error::Error) -> String {
|
||||||
let mut rendered = error.to_string();
|
let mut rendered = error.to_string();
|
||||||
let mut source = error.source();
|
let mut source = error.source();
|
||||||
while let Some(cause) = source {
|
while let Some(cause) = source {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue