seanmonstar/reqwest · error · reqwest::Error
error sending request
Error message
error sending request
What it means
Surfaced by the HTTP/3 client when sending a request over an already-pooled QUIC/H3 stream fails. It is constructed at h3_client/mod.rs:74 by wrapping the error from `pooled.send_request(req).await` in `Kind::Request`. Unlike the generic request error, this one is specific to the `http3` feature path and means the h3 layer (quinn/h3 crate) rejected or aborted the request stream.
Source
Thrown at src/async_impl/h3_client/mod.rs:74
trace!("connecting to {key:?}...");
let (driver, tx) = self.connector.connect(dest).await?;
trace!("saving new pooled connection to {key:?}");
Ok(self.pool.new_connection(lock, driver, tx))
}
async fn send_request(
mut self,
key: Key,
req: Request<Body>,
) -> Result<Response<ResponseBody>, Error> {
let mut pooled = match self.get_pooled_client(key).await {
Ok(client) => client,
Err(e) => return Err(error::request(e)),
};
pooled
.send_request(req)
.await
.map_err(|e| Error::new(Kind::Request, Some(e)))
}
pub fn request(&self, mut req: Request<Body>) -> H3ResponseFuture {
let pool_key = match pool::extract_domain(req.uri_mut()) {
Ok(s) => s,
Err(e) => {
return H3ResponseFuture {
inner: SyncWrapper::new(Box::pin(future::ready(Err(e)))),
}
}
};
H3ResponseFuture {
inner: SyncWrapper::new(Box::pin(self.clone().send_request(pool_key, req))),
}
}
}
impl Service<Request<Body>> for H3Client {View on GitHub (pinned to 17e9bcb51c)
Solutions
- Inspect the wrapped source via `e.source()` to get the real h3/quinn error code (e.g. H3_EXCESSIVE_LOAD, connection error) and act on it.
- Retry with backoff — HTTP/3 stream errors are frequently transient; ensure `Client` retry policy or an outer retry loop handles it.
- If reproducible, fall back to HTTP/2 by building a second Client without `http3_*` and disabling the `http3` code path for that host.
- Verify the server actually advertises `h3` via Alt-Svc / ALPN and that UDP/443 is reachable; HTTP/3 is often silently blocked by networks.
Example fix
// before
let resp = client.get(url).send().await?; // opaque 'error sending request'
// after
match client.get(url).send().await {
Ok(r) => { /* ... */ }
Err(e) if e.is_request() => {
if let Some(src) = e.source() {
log::warn!("h3 send failed: {src}");
}
// retry or fall back to an h2-only client
}
Err(e) => return Err(e),
} Defensive patterns
Strategy: retry
Validate before calling
// No pre-check possible; verify the endpoint speaks h3 first. // Use http3_prior_knowledge only on hosts you control.
Type guard
fn is_h3_send_error(e: &reqwest::Error) -> bool {
e.is_request()
}
Try / catch
match client.get(&url).send().await {
Ok(r) => Ok(r),
Err(e) if e.is_request() => {
if let Some(src) = e.source() { log::warn!("h3 send: {src}"); }
retry_with_backoff().await
}
Err(e) => Err(e),
} Prevention
- Keep an h2-only fallback Client for hosts where HTTP/3 is unreliable.
- Log e.source() so transient stream resets are distinguishable from real failures.
- Confirm UDP/443 egress is allowed in the deployment network before relying on h3.
When it happens
Trigger: Client built with `.http3_prior_knowledge()` or `.http3_only()` (feature `http3` enabled) and the outbound `send_request` on the QUIC stream errors: stream reset by peer, H3 protocol error (e.g. `H3_EXCESSIVE_LOAD`), GOAWAY from server, max stream count reached, or the pooled connection died mid-send.
Common situations: Talking to an HTTP/3 endpoint that rate-limits or rejects excessive streams; server sends GOAWAY and closes the connection; QUIC MTU/UDP blocked partway so h3 stream stalls and aborts; mismatched ALPN so the 'h3' connection isn't really HTTP/3.
Related errors
- error sending request
- error sending request
- HTTP/3 only supports 'https' or 'h3' schemes, got: {}
- request or response body error
- error following redirect
AI-assisted analysis of seanmonstar/reqwest@17e9bcb51c (2026-08-06).
Data as JSON: /data/errors/26b1f82131164990.json.
Report an issue: GitHub.