Skip to content

h2::Error hides IO error #862

@konstin

Description

@konstin

In uv, we want to retry streaming downloads on connection reset errors. These errors are common e.g. in GitHub Actions when downloading e.g. a 20MB file.

Our goal is to have a streaming download-and-unpack. We have a loop in which we try the streaming download-and-unpack, and if it fails, inspect the error chain on whether it looks like a retryable network error (https://github.com/astral-sh/uv/blob/22f80ca00d3f5af7d087154e998c886bbea8cee1/crates/uv-python/src/downloads.rs#L945-L993). This is easy for status code errors, where we get a reqwest::Error at the top of the error chain, but during the stream, the error may be nested in several layer of other crates involved in the unpacking, see the example error below.

We want to specifically retry io::ErrorKind::ConnectionReset. But when trying err.downcast_ref::<io::Error>() we don't get anything, as the h2 error wraps the IO error, which is not apparent from the debug output.

Tar(
    TarError {
        desc: "failed to unpack `/home/konsti/.local/share/uv/python/.temp/.tmpBHaUXj/python/bin/python3.12`",
        io: Custom {
            kind: Other,
            error: TarError {
                desc: "failed to unpack `python/bin/python3.12` into `/home/konsti/.local/share/uv/python/.temp/.tmpBHaUXj/python/bin/python3.12`",
                io: Custom {
                    kind: Other,
                    error: reqwest::Error {
                        kind: Decode,
                        source: reqwest::Error {
                            kind: Body,
                            source: Error {
                                inner: ErrorImpl {
                                    kind: Body,
                                    cause: Some(
                                        Error {
                                            kind: Io(
                                                Kind(
                                                    ConnectionReset,
                                                ),
                                            ),
                                        },
                                    ),
                                },
                            },
                        },
                    },
                },
            },
        },
    },
)

I'm also interested in ways to make our transient network error detection (https://github.com/astral-sh/uv/blob/f88aaa8740824b03be36c7ff106a139151452165/crates/uv-client/src/base_client.rs#L983-L1067) more reliable in general, without going through reqwest's error chain.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions