Wrapping `anyhow::Error` in another error loses backtrace
Migrating from failure to anyhow, I encounter an issue where I start losing backtraces when anyhow::Errors are wrapped in another error type:
#[test]
fn backtrace_preserved_after_thiserror_derive() {
#[derive(Debug, thiserror::Error)]
pub enum SomeError {
#[error(transparent)]
Anyhow(anyhow::Error),
}
let mut errs = (0..2).map(|_| anyhow::anyhow!("aaa"));
let wrapped = anyhow::Error::from(SomeError::Anyhow(errs.next().unwrap()));
let notwrapped = errs.next().unwrap();
assert_eq!(wrapped.backtrace().to_string(), notwrapped.backtrace().to_string());
}
It is a kind of usage suggested by the documentation:
https://github.com/dtolnay/thiserror/blob/799b3d33c4e302d20023ea47b524be2c97f03ac5/src/lib.rs#L194-L195
It used to be possible to work around this issue in failure (even on stable, which we are on).
It would seem fine if the backtrace could only be propagated if coming from anyhow.
Is it possible to achieve something like this with the anyhow/thiserror combination?
I found one solution for this problem - add the #[backtrace] attribute to inner anyhow::Error, but it requires a nightly.
By the way, map() is lazy and your test never will pass as there indeed would be two different backtraces generated during next() call. I fixed it by switching to Vec.
#[test]
fn backtrace_preserved_after_thiserror_derive() {
#[derive(Debug, thiserror::Error)]
enum SomeError {
#[error(transparent)]
Anyhow {
#[backtrace] // <-- Passing with this attribute
source: anyhow::Error,
},
}
let mut errs: Vec<_> = (0..2).map(|_| anyhow::anyhow!("aaa")).collect();
let wrapped = anyhow::Error::from(SomeError::Anyhow {
source: errs.pop().unwrap(),
});
let notwrapped = errs.pop().unwrap();
assert_eq!(
wrapped.backtrace().to_string(),
notwrapped.backtrace().to_string()
);
}
Would be great if this could be solved on stable too.