openai/codex · error

Environment variable {env_var} for MCP server '{server_name}

Error message

Environment variable {env_var} for MCP server '{server_name}' is not set

What it means

Starting an MCP server configured with bearer_token_env_var, but std::env::var returned VarError::NotPresent: the named variable does not exist in the Codex process environment at all. The bearer token cannot be constructed, so that server's startup aborts with an error naming both the variable and the server.

Source

Thrown at codex-rs/codex-mcp/src/rmcp_client.rs:849

fn resolve_bearer_token(
    server_name: &str,
    bearer_token_env_var: Option<&str>,
) -> Result<Option<String>> {
    let Some(env_var) = bearer_token_env_var else {
        return Ok(None);
    };

    match env::var(env_var) {
        Ok(value) => {
            if value.is_empty() {
                Err(anyhow!(
                    "Environment variable {env_var} for MCP server '{server_name}' is empty"
                ))
            } else {
                Ok(Some(value))
            }
        }
        Err(env::VarError::NotPresent) => Err(anyhow!(
            "Environment variable {env_var} for MCP server '{server_name}' is not set"
        )),
        Err(env::VarError::NotUnicode(_)) => Err(anyhow!(
            "Environment variable {env_var} for MCP server '{server_name}' contains invalid Unicode"
        )),
    }
}

fn validate_mcp_server_name(server_name: &str) -> Result<()> {
    let re = regex_lite::Regex::new(r"^[a-zA-Z0-9_-]+$")?;
    if !re.is_match(server_name) {
        return Err(anyhow!(
            "Invalid MCP server name '{server_name}': must match pattern {pattern}",
            pattern = re.as_str()
        ));
    }
    Ok(())
}

View on GitHub (pinned to 339751715c)

Solutions

  1. Export the variable in the environment that actually launches Codex: export FOO_TOKEN=<value>, then restart Codex
  2. Verify presence in the exact execution context with printenv FOO_TOKEN (CI step, container exec, service unit)
  3. If Codex is started by a desktop app or service, set the variable where that launcher reads it (launchd plist, systemd Environment=, docker -e)
  4. If the token should be resolved on the remote executor, configure it there instead of locally

Example fix

# before: token lives only in an interactive shell
codex   # FOO_TOKEN unset here

# after
export FOO_TOKEN="$(pass show mcp/foo-token)"
codex
Defensive patterns

Strategy: validation

Validate before calling

# Preflight: every bearer_token_env_var in config must exist
for var in $(grep -oP 'bearer_token_env_var\s*=\s*"\K[^"]+' ~/.codex/config.toml); do
  if [ -z "${!var:-}" ]; then echo "missing env: $var" >&2; exit 1; fi
done

Try / catch

match env::var(env_var) {
    Err(env::VarError::NotPresent) => { /* prompt/login flow or a clear setup message */ }
    other => other,
}

Prevention

When it happens

Trigger: config.toml [mcp_servers.NAME] with bearer_token_env_var = "FOO_TOKEN" where FOO_TOKEN was never exported in the environment of the Codex process: launched from a GUI/desktop app that skips shell profiles, from a different shell/session than where the token was set, via env -i codex, or under sudo which strips the environment.

Common situations: Token set in an interactive shell but Codex launched by systemd/launchd/Docker; .env file not loaded by the launcher; secret kept in a password manager but never exported; variable renamed in config (FOO_TOKEN vs FOO_MCP_TOKEN).

Related errors


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