openai/codex · error
MCP HTTP headers helper returned duplicate header names
Error message
MCP HTTP headers helper returned duplicate header names
What it means
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.
Source
Thrown at codex-rs/rmcp-client/src/http_headers.rs:359
})
.await
.map_err(|_| anyhow!("MCP HTTP headers helper timed out after 10 seconds"))??;
parse_helper_output(output)
}
fn parse_helper_output(stdout: Vec<u8>) -> Result<HeaderMap> {
let stdout = String::from_utf8(stdout)
.map_err(|_| anyhow!("MCP HTTP headers helper wrote non-UTF-8 data"))?;
let mut deserializer = serde_json::Deserializer::from_str(stdout.trim());
let headers = RawHeaderEntries::deserialize(&mut deserializer)
.and_then(|headers| {
deserializer.end()?;
Ok(headers)
})
.map_err(|_| anyhow!("MCP HTTP headers helper must output a JSON object of strings"))?;
if headers.has_exact_duplicate {
return Err(anyhow!(
"MCP HTTP headers helper returned duplicate header names"
));
}
let mut parsed = HeaderMap::with_capacity(headers.entries.len());
for (name, value) in headers.entries {
let name = HeaderName::from_bytes(name.as_bytes())
.map_err(|_| anyhow!("MCP HTTP headers helper returned an invalid header name"))?;
// Helper values replace same-name configured headers; bearer/OAuth owns Authorization.
// Google IAP uses Proxy-Authorization alongside application Authorization. For HTTPS MCP
// URLs it is sent through the forward-proxy tunnel to IAP, not used as CONNECT auth.
if matches!(
name.as_str(),
"accept"
| "authorization"
| "connection"
| "content-encoding"
| "content-length"
| "content-type"View on GitHub (pinned to 339751715c)
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
Example fix
# before
{"X-Api-Key":"a","x-api-key":"b"}
# after
{"X-Api-Key":"b"} Defensive patterns
Strategy: validation
Validate before calling
# Reject duplicate header names, case-insensitively cd "$MCP_CWD" && env -i PATH=/usr/bin:/bin sh -c "$HTTP_HEADERS_HELPER" | jq -e \ '(keys | map(ascii_downcase) | length) == (keys | map(ascii_downcase) | unique | length)' >/dev/null \ && echo names-unique || echo duplicate-header-names
Type guard
fn is_helper_duplicate_headers(error: &anyhow::Error) -> bool {
error.to_string().contains("duplicate header names")
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- MCP HTTP headers helper returned a reserved header
- invalid HTTP header: {0}
- MCP HTTP headers helper containment failed: {error}
- MCP HTTP headers helper failed to start: {error}
- MCP HTTP headers helper process id was unavailable
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/5c2e6d9cf8ca0ab0.
Report an issue: GitHub.