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

  1. Raise the .timeout() value so the whole operation (headers, body transfer, and all redirect hops) fits inside the budget.
  2. If only connection setup should be bounded, use RouteAwareClientPool::with_connect_timeout(...) or HttpClientBuilder::connect_timeout instead of the per-request timeout.
  3. Diagnose the slow phase: DNS, PAC/WPAD resolution, proxy reachability, server latency.
  4. 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

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

Related errors


AI-assisted analysis of openai/codex@339751715c (2026-08-25). Data as JSON: /api/errors/98fce25b9ce21640. Report an issue: GitHub.