{"record":{"id":"5c2e6d9cf8ca0ab0","repo":"openai/codex","slug":"mcp-http-headers-helper-returned-duplicate-header","errorCode":null,"errorMessage":"MCP HTTP headers helper returned duplicate header names","messagePattern":"MCP HTTP headers helper returned duplicate header names","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"codex-rs/rmcp-client/src/http_headers.rs","lineNumber":359,"sourceCode":"    })\n    .await\n    .map_err(|_| anyhow!(\"MCP HTTP headers helper timed out after 10 seconds\"))??;\n\n    parse_helper_output(output)\n}\n\nfn parse_helper_output(stdout: Vec<u8>) -> Result<HeaderMap> {\n    let stdout = String::from_utf8(stdout)\n        .map_err(|_| anyhow!(\"MCP HTTP headers helper wrote non-UTF-8 data\"))?;\n    let mut deserializer = serde_json::Deserializer::from_str(stdout.trim());\n    let headers = RawHeaderEntries::deserialize(&mut deserializer)\n        .and_then(|headers| {\n            deserializer.end()?;\n            Ok(headers)\n        })\n        .map_err(|_| anyhow!(\"MCP HTTP headers helper must output a JSON object of strings\"))?;\n    if headers.has_exact_duplicate {\n        return Err(anyhow!(\n            \"MCP HTTP headers helper returned duplicate header names\"\n        ));\n    }\n    let mut parsed = HeaderMap::with_capacity(headers.entries.len());\n    for (name, value) in headers.entries {\n        let name = HeaderName::from_bytes(name.as_bytes())\n            .map_err(|_| anyhow!(\"MCP HTTP headers helper returned an invalid header name\"))?;\n        // Helper values replace same-name configured headers; bearer/OAuth owns Authorization.\n        // Google IAP uses Proxy-Authorization alongside application Authorization. For HTTPS MCP\n        // URLs it is sent through the forward-proxy tunnel to IAP, not used as CONNECT auth.\n        if matches!(\n            name.as_str(),\n            \"accept\"\n                | \"authorization\"\n                | \"connection\"\n                | \"content-encoding\"\n                | \"content-length\"\n                | \"content-type\"","sourceCodeStart":341,"sourceCodeEnd":377,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/rmcp-client/src/http_headers.rs#L341-L377","documentation":"Header names must be unique. Exact byte-identical duplicate JSON keys are caught during deserialization (has_exact_duplicate — ordinary map parsing would silently collapse them), and names colliding case-insensitively are caught when inserting into the HeaderMap, because HTTP header names are case-insensitive. Either form rejects the helper output rather than silently dropping one value.","triggerScenarios":"Helper JSON containing {\"X-Api-Key\": \"a\", \"X-Api-Key\": \"b\"} (exact duplicate) or {\"X-Api-Key\": \"a\", \"x-api-key\": \"b\"} (case-insensitive duplicate via the parsed-map check).","commonSituations":"Config assembled from multiple sources appending the same header twice; case mismatches between environments (X-API-KEY vs x-api-key); hand-edited JSON with repeated keys that most parsers hide.","solutions":["Deduplicate header names case-insensitively in the helper before printing","Pick a single canonical casing per header","Validate with jq: keys | map(ascii_downcase) | length == (unique | length)","If merging configs, last-write-wins in your own tooling so duplicates never reach the helper"],"exampleFix":"# before\n{\"X-Api-Key\":\"a\",\"x-api-key\":\"b\"}\n\n# after\n{\"X-Api-Key\":\"b\"}","handlingStrategy":"validation","validationCode":"# Reject duplicate header names, case-insensitively\ncd \"$MCP_CWD\" && env -i PATH=/usr/bin:/bin sh -c \"$HTTP_HEADERS_HELPER\" | jq -e \\\n  '(keys | map(ascii_downcase) | length) == (keys | map(ascii_downcase) | unique | length)' >/dev/null \\\n  && echo names-unique || echo duplicate-header-names","typeGuard":"fn is_helper_duplicate_headers(error: &anyhow::Error) -> bool {\n    error.to_string().contains(\"duplicate header names\")\n}","tryCatchPattern":null,"preventionTips":["Canonicalize header-name casing in the helper before printing","Last-write-wins when merging header sources in your own tooling","Remember HTTP header names are case-insensitive when merging configs"],"tags":["rust","mcp","headers-helper","duplicate-keys","http-headers"],"backgroundTag":"duplicate-header-names","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}