openai/codex · error · RouteAwareRequestError
route-aware request timed out
Error message
route-aware request timed out
What it means
The builder's timeout(Duration) sets a whole-request budget that starts before outbound-route resolution and covers proxy/PAC route resolution, pooled-client construction, connection establishment, sending, awaiting the response, every redirect hop, and any rustls fallback retry (doc on RouteAwareRequestBuilder::timeout at route_aware_client_pool.rs:253-258). When any phase misses the deadline (tokio::time::timeout_at) or the deadline has already elapsed between hops, the pool returns RouteAwareRequestError::Timeout rather than a reqwest error.
Source
Thrown at codex-rs/http-client/src/route_aware_client_pool.rs:100
Resolve(#[source] io::Error),
#[error(transparent)]
Build(#[from] BuildRouteAwareHttpClientError),
}
/// Error returned while building, routing, or sending a route-aware request.
#[derive(Debug, thiserror::Error)]
pub enum RouteAwareRequestError {
#[error(transparent)]
Request(#[from] reqwest::Error),
#[error(transparent)]
Route(#[from] RouteAwareClientPoolError),
#[error("failed to build route-aware request: {0}")]
Build(String),
#[error("redirect target uses unsupported URL scheme: {0}")]
UnsupportedRedirectScheme(String),
#[error("too many redirects")]
TooManyRedirects,
#[error("route-aware request timed out")]
Timeout,
}
impl RouteAwareRequestError {
/// Classifies transport, proxy, and certificate failures without exposing request details.
pub fn failure_class(&self) -> Option<RouteFailureClass> {
if self.is_timeout() {
return Some(RouteFailureClass::ConnectTimeout);
}
if self.status() == Some(StatusCode::PROXY_AUTHENTICATION_REQUIRED) {
return Some(RouteFailureClass::ProxyAuthenticationRequired);
}
if let Self::Route(RouteAwareClientPoolError::Resolve(error)) = self
&& let Some(source) = error.get_ref()
&& source.is::<rustls::Error>()
{
return Some(RouteFailureClass::TlsError);
}View on GitHub (pinned to 339751715c)
Solutions
- Raise the .timeout() value so the whole operation (headers, body transfer, and all redirect hops) fits inside the budget.
- If only connection setup should be bounded, use RouteAwareClientPool::with_connect_timeout(...) or HttpClientBuilder::connect_timeout instead of the per-request timeout.
- Diagnose the slow phase: DNS, PAC/WPAD resolution, proxy reachability, server latency.
- Treat as transient: check is_timeout() and retry with a fresh deadline and backoff.
Example fix
// before: whole-request budget too small for a large download let resp = pool.get(url).timeout(Duration::from_secs(5)).send().await; // Err(RouteAwareRequestError::Timeout) // after: bound only connection establishment; do not cap the body transfer let pool = RouteAwareClientPool::with_connect_timeout(factory, route_class, Duration::from_secs(10)); let resp = pool.get(url).send().await?;
Defensive patterns
Strategy: retry
Validate before calling
// Before the real request, confirm the route/proxy layer answers within a small budget
let probe = pool.head(&health_url).timeout(Duration::from_secs(2)).send().await;
if probe.is_err() {
// fix proxy/network configuration before issuing the expensive request
} Type guard
fn is_request_timeout(e: &RouteAwareRequestError) -> bool { e.is_timeout() } Try / catch
if let Err(e) = &result {
if e.is_timeout() {
// backoff, then rebuild the request so timeout_mut() gets a fresh Duration
} else {
return Err(result.err().unwrap());
}
} Prevention
- Budget the timeout for the total operation: route resolution + connect + headers + body + redirects.
- Use with_connect_timeout when only slow connects are the concern.
- Never reuse an elapsed deadline across retries; construct a new request each attempt.
When it happens
Trigger: Calling .timeout(d) on the builder and awaiting send() when: proxy-route resolution (PAC script, WPAD, system proxy) hangs; TLS handshake or TCP connect is slow; the server responds slowly or the body transfer is large; redirect chains consume the budget across hops (remaining time is recomputed per hop and returned as Timeout once it hits zero).
Common situations: Timeout tuned too tight for large uploads/downloads; corporate PAC endpoints that block; misconfigured or unreachable system proxy; retrying with a deadline that is already spent; confusing the per-request .timeout() with with_connect_timeout(), which only bounds connection establishment.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timeout
- Luna Responses WebSocket connection timed out
- network error: {0}
- too many redirects
- standalone Codex updater request failed with status {status}
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/98fce25b9ce21640.
Report an issue: GitHub.