fix: preserve the underlying error when a reqwest request fails without a status#2669
Merged
Sebastian Thiel (Byron) merged 1 commit intoJun 23, 2026
Conversation
…ut a status When the blocking `reqwest` HTTP backend failed without an HTTP status -- e.g. a connection or TLS failure, as when `http-client-reqwest` is used without a TLS feature against an `https://` URL -- it stringified the `reqwest::Error` before wrapping it in an `io::Error`. That dead-ended `source()`, so callers only saw "An IO error occurred when talking to the server" with no way to reach the real cause. Keep the `reqwest::Error` as the `io::Error`'s source (via `io::Error::other`) so the underlying cause is preserved. The HTTP-status branch is unchanged. Adds a `reqwest`-only regression test that drives a refused connection and asserts the error source is retained; it runs under the `http-client-reqwest` test configuration, which also adds http test coverage for that backend.
82dea98 to
1228b87
Compare
Sebastian Thiel (Byron)
approved these changes
Jun 23, 2026
Sebastian Thiel (Byron)
left a comment
Member
There was a problem hiding this comment.
Thanks a lot!
This is a rare PR as I couldn't change a thing 😁.
Here my local results:
Before
❯ cargo run -p gix --example clone --features blocking-http-transport-reqwest https://github.com/samvv/evcape.git delme
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
Running `target/debug/examples/clone 'https://github.com/samvv/evcape.git' delme`
Url: "https://github.com/samvv/evcape.git"
Cloning "https://github.com/samvv/evcape.git" into "delme"...
Error: An IO error occurred when talking to the server
Caused by:
error sending request for url (https://github.com/samvv/evcape.git/info/refs?service=git-upload-pack)
After
❯ cargo run -p gix --example clone --features blocking-http-transport-reqwest https://github.com/samvv/evcape.git delme
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
Running `target/debug/examples/clone 'https://github.com/samvv/evcape.git' delme`
Url: "https://github.com/samvv/evcape.git"
Cloning "https://github.com/samvv/evcape.git" into "delme"...
Error: An IO error occurred when talking to the server
Caused by:
0: error sending request for url (https://github.com/samvv/evcape.git/info/refs?service=git-upload-pack)
1: client error (SendRequest)
2: connection closed before message completed
c8fbf29
into
GitoxideLabs:main
56 of 62 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Addresses #2140. When the blocking
reqwestHTTP backend failed a request without an HTTP status — a connection or TLS failure, e.g. usinghttp-client-reqwestwithout a TLS feature against anhttps://URL — it stringified thereqwest::Error(err.to_string()) before wrapping it in anio::Error. AStringhas nosource(), so the error chain dead-ended at "An IO error occurred when talking to the server", hiding the real cause — the empty.source()the reporter observed.The outer wrapper (
client::Error::Io(#[from] io::Error)) already preserves theio::Erroras its source; the only break was this stringification.This resolves the part Sebastian Thiel (@Byron) identified — "what I'd be missing here is a clearer error message". It does not make plain
http-client-reqwestperform HTTPS (that still requires a TLS feature); it makes the reason a request failed visible instead of swallowed.How
In the no-status branch, keep the
reqwest::Erroras theio::Error's source viaio::Error::other(err)rather than stringifying it. The HTTP-status branch is unchanged (it still reportsReceived HTTP status N)..source()now walks into reqwest's chain and surfaces the underlying cause.Tests
Adds a regression test (
tests/blocking-transport-http-reqwest.rs) that drives a refused TCP connection through the reqwest backend and asserts the resulting error preserves itssource(). This covers the no-status (connection-failure) path specifically — the existing reqwest http tests (run viablocking-transport.rs) already cover status-based errors like 401/404/500. It's wired as a[[test]]withrequired-features = ["http-client-reqwest", "maybe-async/is_sync"]so it builds and runs only under the reqwest test configuration.Verification
source()isNone; post-fixSome).cargo clippy -p gix-transport --features http-client-reqwest,maybe-async/is_sync --testsandcargo fmt --checkare clean;Cargo.lockis unchanged (no dependency change).Some(status)branch was only refactored (same kind, same message), so the existing http status tests are unaffected.Scope is reqwest-specific — the reporter confirmed the curl backend works.