{"id":"26b1f82131164990","repo":"seanmonstar/reqwest","slug":"error-sending-request","errorCode":null,"errorMessage":"error sending request","messagePattern":"error sending request","errorType":"exception","errorClass":"reqwest::Error","httpStatus":null,"severity":"error","filePath":"src/async_impl/h3_client/mod.rs","lineNumber":74,"sourceCode":"        trace!(\"connecting to {key:?}...\");\n        let (driver, tx) = self.connector.connect(dest).await?;\n        trace!(\"saving new pooled connection to {key:?}\");\n        Ok(self.pool.new_connection(lock, driver, tx))\n    }\n\n    async fn send_request(\n        mut self,\n        key: Key,\n        req: Request<Body>,\n    ) -> Result<Response<ResponseBody>, Error> {\n        let mut pooled = match self.get_pooled_client(key).await {\n            Ok(client) => client,\n            Err(e) => return Err(error::request(e)),\n        };\n        pooled\n            .send_request(req)\n            .await\n            .map_err(|e| Error::new(Kind::Request, Some(e)))\n    }\n\n    pub fn request(&self, mut req: Request<Body>) -> H3ResponseFuture {\n        let pool_key = match pool::extract_domain(req.uri_mut()) {\n            Ok(s) => s,\n            Err(e) => {\n                return H3ResponseFuture {\n                    inner: SyncWrapper::new(Box::pin(future::ready(Err(e)))),\n                }\n            }\n        };\n        H3ResponseFuture {\n            inner: SyncWrapper::new(Box::pin(self.clone().send_request(pool_key, req))),\n        }\n    }\n}\n\nimpl Service<Request<Body>> for H3Client {","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/seanmonstar/reqwest/blob/17e9bcb51c46edebfb6f5f2f5184b51dac4b3a7d/src/async_impl/h3_client/mod.rs#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nlet resp = client.get(url).send().await?; // opaque 'error sending request'\n\n// after\nmatch client.get(url).send().await {\n    Ok(r) => { /* ... */ }\n    Err(e) if e.is_request() => {\n        if let Some(src) = e.source() {\n            log::warn!(\"h3 send failed: {src}\");\n        }\n        // retry or fall back to an h2-only client\n    }\n    Err(e) => return Err(e),\n}","handlingStrategy":"retry","validationCode":"// No pre-check possible; verify the endpoint speaks h3 first.\n// Use http3_prior_knowledge only on hosts you control.\n","typeGuard":"fn is_h3_send_error(e: &reqwest::Error) -> bool {\n    e.is_request()\n}\n","tryCatchPattern":"match client.get(&url).send().await {\n    Ok(r) => Ok(r),\n    Err(e) if e.is_request() => {\n        if let Some(src) = e.source() { log::warn!(\"h3 send: {src}\"); }\n        retry_with_backoff().await\n    }\n    Err(e) => Err(e),\n}","preventionTips":["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."],"tags":["http3","quic","network","request"],"analyzedSha":"17e9bcb51c46edebfb6f5f2f5184b51dac4b3a7d","analyzedAt":"2026-08-06T01:23:05.134Z","schemaVersion":2}