Skip to content

hyper::Error prints its first cause in its Display impl, causes duplicate printing in many scenarios #2771

Closed
@lilyball

Description

@lilyball

Version
hyper v0.14.17

Description
hyper::Error's Display impl will print its immediate cause in a "{description}: {cause}" format. The problem is this screws with anything else that tries to print an error along with its causes, as the immediate cause is also the returned value from .source().

For example, if I were to print an error with tracing's built-in support, e.g. error!(err as &dyn StdError, "message"), the resulting log line includes err=error trying to connect: tcp connect error: Connection refused (os error 61) err.sources=[tcp connect error: Connection refused (os error 61), Connection refused (os error 61)]. Or if I wrap this in an eyre::Report and print that with {:#} (which includes the cause chain), I get error trying to connect: tcp connect error: Connection refused (os error 61): tcp connect error: Connection refused (os error 61): Connection refused (os error 61) which is even more confusing.

This appears to be an intentional decision, especially as it has a .message() accessor that returns an impl Display that is just the message. But it's one that is very frustrating. Ideally hyper::Error would just not include its cause, relying on the user to opt in to showing causes. Alternatively it could just skip the first cause in the .source() accessor, though that means users who want to actually inspect that cause (e.g. to downcast it) can't (though in the case of the quoted error, its cause is not a public type and so there's nothing I can reasonably do with it besides just printing it).

Changing this behavior will surprise anyone who is currently working around it, though I suspect it will simply produce better behavior for most users who just log the error and move on. My preferred solution of "just don't print the cause" does make the message() accessor pointless, though that could simply be deprecated without having to do a breaking change.

If you really don't want to make this change, then at least please change the message() accessor to actually be an impl StdError + Send + Sync such that I can then pass that to whatever I'm using that logs causes. The resulting error from this would of course retain the initial cause as the source(). Please also do this even if you decide the right solution is to just skip the initial cause in source(), so I can still get the formatting I want without having to reinvent error chains (especially when dealing with stuff like eyre::Report's {:?} printing).

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: bug. Something is wrong. This is bad!

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions