openai/codex · error · anyhow::Error

Agent Identity only supports production and staging ChatGPT

Error message

Agent Identity only supports production and staging ChatGPT environments

What it means

parse_header_name feeds the configured string to rama_http's HeaderName::from_bytes, which accepts only RFC 7230 token characters: ASCII letters and digits plus !#$%&'*+-.^_`|~. Spaces, colons, other delimiters, control bytes, non-ASCII characters, or an empty string all fail. The helper is shared by match.headers keys, strip_request_headers entries, and inject_request_headers names, and runs during both validation and hook compilation.

Source

Thrown at codex-rs/agent-identity/src/lib.rs:70

    Staging,
}

impl ChatGptEnvironment {
    pub fn from_chatgpt_base_url(chatgpt_base_url: &str) -> Result<Self> {
        match chatgpt_base_url.trim_end_matches('/') {
            "https://chatgpt.com"
            | "https://chatgpt.com/backend-api"
            | "https://chatgpt.com/codex"
            | "https://chatgpt.com/backend-api/codex"
            | "https://chat.openai.com"
            | "https://chat.openai.com/backend-api"
            | "https://chat.openai.com/codex"
            | "https://chat.openai.com/backend-api/codex" => Ok(Self::Production),
            "https://chatgpt-staging.com"
            | "https://chatgpt-staging.com/backend-api"
            | "https://chatgpt-staging.com/codex"
            | "https://chatgpt-staging.com/backend-api/codex" => Ok(Self::Staging),
            _ => anyhow::bail!(
                "Agent Identity only supports production and staging ChatGPT environments"
            ),
        }
    }

    pub fn chatgpt_base_url(self) -> &'static str {
        match self {
            Self::Production => "https://chatgpt.com/backend-api",
            Self::Staging => "https://chatgpt-staging.com/backend-api",
        }
    }

    pub fn agent_identity_authapi_base_url(self) -> &'static str {
        match self {
            Self::Production => PROD_AGENT_IDENTITY_AUTHAPI_BASE_URL,
            Self::Staging => STAGING_AGENT_IDENTITY_AUTHAPI_BASE_URL,
        }
    }

View on GitHub (pinned to 339751715c)

Solutions

  1. Keep the bare token form of the name: authorization, x-custom-header
  2. Strip a trailing colon or surrounding whitespace from names copied out of header lines
  3. Verify names programmatically with the same rule: http::HeaderName::from_bytes(name.as_bytes()).is_ok()

Example fix

// config.toml — before
strip_request_headers = ["Authorization:"]

// after
strip_request_headers = ["authorization"]
Defensive patterns

Strategy: type-guard

Validate before calling

let bad: Vec<_> = hook.actions.strip_request_headers.iter()
    .chain(hook.matcher.headers.keys().map(String::as_str))
    .filter(|n| !is_valid_header_name(n))
    .collect();
if !bad.is_empty() {
    return Err(anyhow!("invalid header names: {bad:?}"));
}

Type guard

fn is_valid_header_name(name: &str) -> bool {
    http::HeaderName::from_bytes(name.as_bytes()).is_ok()
}

Try / catch

let name = HeaderName::from_bytes(raw.as_bytes())
    .map_err(|err| anyhow!("invalid header name {raw:?}: {err}"))?;

Prevention

When it happens

Trigger: strip_request_headers = ["Authorization:"] (trailing colon copied from a header line), a match.headers key like "X-Custom Header" (space), name = "" (empty), or a non-ASCII name such as a localized header. Any of these aborts validate_mitm_hook_config or the later compile step.

Common situations: Pasting 'Authorization: Bearer x' and keeping the colon; smart quotes or invisible unicode introduced by docs/chat paste; whitespace or CRLF artifacts in generated configs.

Related errors


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