Finding the origin of a Result::Err

Somewhere deep inside a deserialization library I'm using, something returns a Result::Err with a very non-helpful error message. It gets passed on through several layers, some of them macro-generated. When I try to .unwrap() it, I get the location of the .unwrap() call as the origin of the panic. Is there some way to track the location of where the original Result::Err was constructed?

This is a third-party library, I do not have the capacity to refactor it to use a more verbose error handling library.

The error type is std::io::error::Error. Is it possible to set a debugger breakpoint or something upon its creation?

unfortunately, in rust, an error trace is not enforced, it's up to the library author to implement the functionality.

if the error you got implemented the std::error::Error trait, you can try walk the source() chain. again, it's not guaranteed to be available, it's a matter of quality of implementation for the specific library.

you don't have to write the code yourself, you can use any error reporting library, such as anyhow, just convert the error into anyhow::Error and you can print it using the Debug format, something like this:

let e = some_library_api().unwrap_err();
let e = anyhow::Error::new(e);
println!("{:?}", e);

if the error type is well designed, this should print out the full error trace.

note, if the original error didn't capture a stack trace, anyhow will add one, but it is not useful.

yes, it's possible, but you might need set multiple breakpoints since it has several constructors, and you don't know which one is actually called.

for errors carrying an OS specific error code, it is most likely to be constructed using Error::last_os_error() or Error::from_raw_os_error(). so try these first. if they didn't work, the std::io::Error is probably used as a wrapper for other error types, in which case try Error::new() and Error::other(). another potential constructor is the From<ErrorKind> impl block, so try that one if the others don't work.

there are other From conversions, but they are unlikely to be used by a third party library.

What exact kind of “deserialization library” do you mean here?

If it’s a serde deserializer or data type, then serde_path_to_error - Rust may be able to help; it is a wrapper that adds more context information through the associated deserialization error type.

I do - env::set_var("RUST_BACKTRACE", "1"), it helps sometimes.

It's rosrust, a library for a format exclusively used in robotics.

I ended up finding my bug by patching it to include debugger break points on error conditions using unbug, but in the end that's basically the same as adding print debugging everywhere.

That just points at the place where you .unwrap() the Result, and provides no clue as to where the Result::Err originated.

The error type would need to use Backtrace or the equivalent when creating the error or an equivalent. Many common "generic" error libraries like anyhow will do this, but custom "library" errors made to be easily matched generally won't. The ideal is that it provides enough information that you didn't need to, but creating good errors is hard :disappointed_face:

The "nice" answer here is to submit a patch that gives more useful information in the error, but if I'm being lazy just running under a debugger and putting a breakpoint on every (suspect) line with the error value you're seeing would be the next go-to I would try (this depends on you having a debugger setup working of course)