I would recommend the `anyhow` crate and use of anyhow::Context to annotate errors on the return path within applications, like:
falliable_func().context("failed to frob the peanut")?
Combine that with the `thiserror` crate for implementing errors within a library context. `thiserror` makes it easy to implement structured errors which embed other errors, and plays well with `anyhow`.