openai/codex · error · RouteAwareRequestError

redirect target uses unsupported URL scheme

Error message

redirect target uses unsupported URL scheme: {0}

What it means

RouteAwareRequestError::UnsupportedRedirectScheme is raised during the pool's manual redirect following (route_aware_client_pool.rs:635-639). Under RespectSystemProxy (or TLS-backend fallback) reqwest's internal redirects are disabled so each hop gets its own route decision; when a 301/302/303/307/308 Location resolves to a URL whose scheme is not http or https, the pool refuses to follow and returns this error with the offending scheme as its string.

Solutions

  1. Trace the redirect chain (curl -IL) and fix the server's Location target to an http/https URL
  2. If the target scheme is legitimately outside HTTP, handle it at the application layer instead of expecting the pool to follow it
  3. Use a pool built with RouteAwareClientPool::new_without_redirects so each 3xx response is returned to your code and you decide which hops to follow
  4. Do not retry: the same Location will fail identically

Example fix

// before
let pool = RouteAwareClientPool::new(factory, ClientRouteClass::Api);
let resp = pool.get(url).send().await?; // UnsupportedRedirectScheme("ftp")

// after: observe redirects yourself and follow only http/https
let pool = RouteAwareClientPool::new_without_redirects(factory, ClientRouteClass::Api);
let resp = pool.get(url).send().await?;
if resp.status().is_redirection() { /* read Location, decide, re-send */ }
Defensive patterns

Strategy: try-catch

Type guard

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

Try / catch

Err(RouteAwareRequestError::UnsupportedRedirectScheme(scheme)) => {
    // surface the original destination; do not follow non-HTTP schemes
    Err(Upstream::RedirectScheme { scheme, from: original_url })
}

Prevention

When it happens

Trigger: Sending through a RouteAwareClientPool that follows redirects manually when the server answers 3xx with Location: ftp://..., file://..., or any non-http(s) URI: the scheme check at route_aware_client_pool.rs:635 fails and the send aborts instead of following the hop.

Common situations: Legacy download endpoints still redirecting to ftp://, misconfigured server or CDN redirect rules, open-redirect probes pointing at exotic schemes, local dev servers redirecting to internal tooling URLs.

Related errors


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

Appendix: source

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

/// 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);
        }
        if let Self::Route(RouteAwareClientPoolError::Resolve(error)) = self
            && let Some(source) = error.get_ref()

View on GitHub (pinned to 339751715c)