{"record":{"id":"4039729206bb4a35","repo":"openai/codex","slug":"failed-to-build-route-aware-request-0","errorCode":null,"errorMessage":"failed to build route-aware request: {0}","messagePattern":"failed to build route-aware request: (.+?)","errorType":"exception","errorClass":"RouteAwareRequestError","httpStatus":null,"severity":"error","filePath":"codex-rs/http-client/src/route_aware_client_pool.rs","lineNumber":94,"sourceCode":"}\n\n/// Error returned when selecting a route or constructing its pooled HTTP client.\n#[derive(Debug, thiserror::Error)]\npub enum RouteAwareClientPoolError {\n    #[error(\"failed to resolve the outbound proxy route: {0}\")]\n    Resolve(#[source] io::Error),\n    #[error(transparent)]\n    Build(#[from] BuildRouteAwareHttpClientError),\n}\n\n/// Error returned while building, routing, or sending a route-aware request.\n#[derive(Debug, thiserror::Error)]\npub enum RouteAwareRequestError {\n    #[error(transparent)]\n    Request(#[from] reqwest::Error),\n    #[error(transparent)]\n    Route(#[from] RouteAwareClientPoolError),\n    #[error(\"failed to build route-aware request: {0}\")]\n    Build(String),\n    #[error(\"redirect target uses unsupported URL scheme: {0}\")]\n    UnsupportedRedirectScheme(String),\n    #[error(\"too many redirects\")]\n    TooManyRedirects,\n    #[error(\"route-aware request timed out\")]\n    Timeout,\n}\n\nimpl RouteAwareRequestError {\n    /// Classifies transport, proxy, and certificate failures without exposing request details.\n    pub fn failure_class(&self) -> Option<RouteFailureClass> {\n        if self.is_timeout() {\n            return Some(RouteFailureClass::ConnectTimeout);\n        }\n        if self.status() == Some(StatusCode::PROXY_AUTHENTICATION_REQUIRED) {\n            return Some(RouteFailureClass::ProxyAuthenticationRequired);\n        }","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/http-client/src/route_aware_client_pool.rs#L76-L112","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","solutions":["Read the embedded string: it is the http::Error or serde_json::Error Display and names the offending part","Validate header names and values before building: HeaderName::try_from / HeaderValue::try_from reject CR/LF and invalid bytes","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","Reject the request as malformed input instead of retrying: the same input fails identically"],"exampleFix":"// before\nlet resp = pool.get(url).header(\"X-Trace\", user_input).send().await?; // Build(\"invalid header value\") on CR/LF\n\n// after: validate at the boundary, reject early\nlet value = http::HeaderValue::from_str(&user_input)\n    .map_err(|e| BadRequest::invalid_header(e))?;\nlet resp = pool.get(url).header(\"X-Trace\", value).send().await?;","handlingStrategy":"validation","validationCode":"// Validate header parts exactly as the builder will\nfn header_parts_valid(name: &str, value: &str) -> bool {\n    http::HeaderName::try_from(name).is_ok() && http::HeaderValue::try_from(value).is_ok()\n}\n\n// Pre-serialize JSON so failures surface at your call site\nfn json_body(value: &impl Serialize) -> Result<Vec<u8>, String> {\n    serde_json::to_vec(value).map_err(|e| e.to_string())\n}","typeGuard":"fn is_request_build_error(e: &RouteAwareRequestError) -> bool {\n    matches!(e, RouteAwareRequestError::Build(_))\n}","tryCatchPattern":"Err(RouteAwareRequestError::Build(reason)) => {\n    // deterministic bad input: reject, never retry\n    return Err(BadRequest::malformed(reason));\n}","preventionTips":["Reject CR/LF and control bytes in any user-controlled header value before it reaches .header()","Validate header names against an allowlist when they come from config","Pre-serialize JSON payloads at the boundary to keep serialization errors in your own error type","Fuzz header-heavy request paths; builder errors surface at send time, which is late"],"tags":["request","http-headers","validation","rust"],"backgroundTag":"invalid-http-header","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}