-
Notifications
You must be signed in to change notification settings - Fork 351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Error wrapping and conversion between modules #68
Comments
Seems like tm-rs is getting some activity on this front soon: informalsystems/tendermint-rs#669. Since the anomaly crate will become deprecated, we'll also need to move away to something else. |
Here's an example of moving from
Together those should eliminate the need for any error wrapping/conversions entirely. Here's an example of what an Note that we also plan on moving Abscissa to use |
After playing with eyre (#493), and given our experience with anomaly so far, here are some observations: We cannot get rid of all 'awkwardness' around error handlingWith this issue we originally wanted to get rid of patterns such as (...
port_id: String,
)
....
port_id: port_id
.parse()
.map_err(|e| crate::ics04_channel::error::Kind::IdentifierError.context(e))?, and replace them with: port_id: port_id.parse()?, This replacement is possible with eyre, but not always desirable. Sometimes we may want to chain errors and wrap them into one another to capture both the high-level error cause as well as the lower-level cause(s). In the example above, error chaining is useful because it will report on a high level error (namely, channel msg. creation) and also point to an underlying error (identifier error in parsing the port id). Below is an example where error chaining is not useful, and we can get rid of it without impacting debugging/usability: With error wrappingFor the The output in the error case would look like this:
Without error wrappingThe code becomes: let res = list_keys(opts);
// ... Output becomes:
As a rule of thumb, I think that the deeper we are in the stack, the more we may wish to chain errors (with wrap/map) instead of simply propagating them. Some advantages of using eyreWe can simplify by removing the
|
I'd say it's pretty common to define aliases of I think it's especially nice for consumers of a downstream crate which need to impl a trait that uses the downstream crate's
|
Thank you @tarcieri, I wasn't familiar that this is a common approach. The example with std::io::Result drives the point home very well. I guess the second disadvantage I mentioned ("moving away from core") is not so much of a problem. The other disadvantage I mentioned above has to do with the difficulty of matching or handling a eyre::Report. As stated in their docs:
In the ibc-rs modules, most errors are unrecoverable, and I can't think of a case where we'd need to handle an error. In the relayer, however, there is a subset of all possible errors we'd like to handle, for example:
All of these are network calls. That being said, it will not be impossible to match the error and retry (just not very clean). |
Agreed. A potential solution for tackling the verbosity of wrapping the parse error in a high-level error would be to introduce a fn parse_port_id(port_id: &str) -> Result<PortId, crate::ics04_channel::error::Error> {
port_id.parse().map_err(crate::ics04_channel::error::Error::InvalidIdentifier)
} which would be used as: ...
port_id: parse_port_id(&port_id)?,
... with the #[derive(Error)]
enum Error {
InvalidIdentifier(ValidationError),
} |
Agreed. In my opinion, library APIs should return specific, nested errors via a |
@adizere in general it seems people use a combination of error handling strategies, selecting which one most appropriately fits their needs for each given scenario. This generally looks like using an |
I'd argue that introducing a specific error type is both cleaner and more useful as it allows the enclosing function to return a |
Agreed. |
Regarding |
@adizere Thanks for the great write-up!
In general, I agree with your conclusions, and in light of the comment of yours quoted above, I would suggest we do the following:
This would let us: a) precisely define all the various errors and their relationship in the low-level What do you think? |
This issue is no longer relevant with the introduction of flex-error #988 |
Summary
Current relayer uses the
anomaly
crate for defining error "kinds" and handling errors. It is not clear if and how conversion between different errors or "kinds" can be done.Problem Definition
There are interactions between IBC modules where error handling is currently awkward. For example the channel message creation (ICS4) verifies that input is properly formatted by invoking validation part of the "host" (ICS24):
This would be nice:
For Admin Use
The text was updated successfully, but these errors were encountered: