thiserror icon indicating copy to clipboard operation
thiserror copied to clipboard

Wrapping `anyhow::Error` in another error loses backtrace

Open Ten16 opened this issue 3 years ago • 2 comments

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?

Ten16 avatar Jun 14 '22 16:06 Ten16

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.

mhnap avatar Aug 28 '24 11:08 mhnap