openai/codex · error · RouteAwareRequestError

failed to build route-aware request: {0}

Error message

failed to build route-aware request: {0}

What it means

RouteAwareRequestError::Build is stored by RouteAwareRequestBuilder when a builder method cannot prepare the request: .header() records the http::Error string for an invalid header name or value (route_aware_client_pool.rs:246) and .json() records the serde_json::to_vec error string (route_aware_client_pool.rs:281). send() then yields it via 'self.request?' (line 312). It is deterministic: nothing network-related is involved, so retrying cannot succeed.

Source

Thrown at codex-rs/http-client/src/route_aware_client_pool.rs:94

}

/// Error returned when selecting a route or constructing its pooled HTTP client.
#[derive(Debug, thiserror::Error)]
pub enum RouteAwareClientPoolError {
    #[error("failed to resolve the outbound proxy route: {0}")]
    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);
        }

View on GitHub (pinned to 339751715c)

Solutions

  1. Read the embedded string: it is the http::Error or serde_json::Error Display and names the offending part
  2. Validate header names and values before building: HeaderName::try_from / HeaderValue::try_from reject CR/LF and invalid bytes
  3. For JSON failures, fix or pre-serialize the payload with serde_json::to_vec and attach it with .body() so the error surfaces at your call site
  4. Reject the request as malformed input instead of retrying: the same input fails identically

Example fix

// before
let resp = pool.get(url).header("X-Trace", user_input).send().await?; // Build("invalid header value") on CR/LF

// after: validate at the boundary, reject early
let value = http::HeaderValue::from_str(&user_input)
    .map_err(|e| BadRequest::invalid_header(e))?;
let resp = pool.get(url).header("X-Trace", value).send().await?;
Defensive patterns

Strategy: validation

Validate before calling

// Validate header parts exactly as the builder will
fn header_parts_valid(name: &str, value: &str) -> bool {
    http::HeaderName::try_from(name).is_ok() && http::HeaderValue::try_from(value).is_ok()
}

// Pre-serialize JSON so failures surface at your call site
fn json_body(value: &impl Serialize) -> Result<Vec<u8>, String> {
    serde_json::to_vec(value).map_err(|e| e.to_string())
}

Type guard

fn is_request_build_error(e: &RouteAwareRequestError) -> bool {
    matches!(e, RouteAwareRequestError::Build(_))
}

Try / catch

Err(RouteAwareRequestError::Build(reason)) => {
    // deterministic bad input: reject, never retry
    return Err(BadRequest::malformed(reason));
}

Prevention

When it happens

Trigger: Calling pool.post(url).header(name, value) where the name contains invalid characters (spaces, non-ASCII) or the value contains control characters/newlines (CRLF injection), or .json(&value) where the value's Serialize impl returns an error during serde_json::to_vec.

Common situations: Passing user- or config-supplied strings straight into header values without sanitization, dynamic header names built from templates, custom Serialize types that error on edge-case data (NaN sentinels, exhausted state).

Related errors


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