openai/codex · error · StreamableHttpClientAdapterError
invalid HTTP header: {0}
Error message
invalid HTTP header: {0} What it means
Header(String) wraps a failure to build an http::HeaderValue for headers the adapter must construct itself: Accept, Content-Type, Authorization: Bearer <token>, Mcp-Session-Id, and Last-Event-Id (all set through insert_header). HeaderValue::from_str rejects anything outside visible ASCII plus space/tab — control bytes, CR/LF, DEL, non-ASCII — so the culprit is almost always a token or session id carrying a stray newline or invisible character.
Source
Thrown at codex-rs/rmcp-client/src/http_client_adapter.rs:110
cancellations: Arc<Mutex<HashMap<RequestId, oneshot::Sender<()>>>>,
}
impl Drop for EventStreamCancellation {
fn drop(&mut self) {
self.cancellations
.lock()
.unwrap_or_else(PoisonError::into_inner)
.remove(&self.request_id);
}
}
#[derive(Debug, thiserror::Error)]
pub(crate) enum StreamableHttpClientAdapterError {
#[error("streamable HTTP session expired with 404 Not Found")]
SessionExpired404,
#[error(transparent)]
HttpRequest(#[from] ExecServerError),
#[error("invalid HTTP header: {0}")]
Header(String),
#[error("MCP response body exceeds {maximum_bytes} bytes")]
ResponseTooLarge { maximum_bytes: usize },
}
impl StreamableHttpClientAdapter {
pub(crate) fn new(
http_client: Arc<dyn HttpClient>,
default_headers: HeaderMap,
auth_provider: Option<SharedAuthProvider>,
has_configured_headers: bool,
redirect_mode: StreamableHttpRedirectMode,
initialize_deadline: Arc<Mutex<Option<Instant>>>,
) -> Self {
Self {
http_client: Arc::new(SameOriginRedirectHttpClient::new(http_client)),
default_headers,
auth_provider,View on GitHub (pinned to 339751715c)
Solutions
- trim() tokens/session ids before passing them to the client
- Validate the value is visible-ASCII (bytes 0x20–0x7E plus tab) before the call
- If a server-issued id is the culprit, capture it exactly as the server sent it and report the server bug
- Add a unit test asserting your token source never contains \r or \n
Example fix
// before
let token = std::fs::read_to_string("token.txt")?; // trailing '\n' -> invalid HTTP header
// after
let token = std::fs::read_to_string("token.txt")?.trim().to_string();
assert!(token.bytes().all(|b| b == b'\t' || (0x20..0x7f).contains(&b))); Defensive patterns
Strategy: validation
Validate before calling
fn assert_header_value_safe(value: &str) {
assert!(
value.bytes().all(|b| b == b'\t' || (0x20..0x7f).contains(&b)),
"value contains bytes invalid in an HTTP header"
);
}
// apply to every token/session id before handing it to the client
let token = token.trim();
assert_header_value_safe(&token); Type guard
fn is_valid_header_value(value: &str) -> bool {
value.bytes().all(|b| b == b'\t' || (0x20..0x7f).contains(&b))
} Try / catch
// When the client error surfaces:
if let Some(source) = find_header_error(&error) {
// strip and re-validate the offending token/session id, then retry once
} Prevention
- Always trim() secrets read from files or env vars
- Never build Authorization values from unvalidated user input
- Add tests asserting token sources contain no \r/\n
- Store session ids opaquely; do not round-trip them through lossy encodings
When it happens
Trigger: An auth token containing \n or other control bytes when rmcp passes it to post_message/get_stream/delete_session; an Mcp-Session-Id or Last-Event-Id returned by a server containing spaces, non-ASCII, or control characters.
Common situations: Secrets read from files or env vars with trailing newlines (the classic $(cat token.txt) mistake); tokens copy-pasted with invisible Unicode; a misbehaving server issuing session ids with unusual bytes; values interpolated from unvalidated config.
Related errors
- MCP HTTP headers helper returned duplicate header names
- MCP HTTP headers helper returned a reserved header
- Agent Identity only supports production and staging ChatGPT
- invalid remote control account id header: {err}
- invalid requirement for MCP server `{server_name}` (set by {
AI-assisted analysis of openai/codex@339751715c (2026-08-25).
Data as JSON: /api/errors/8d9407167eadb3fd.
Report an issue: GitHub.