Hmbown/CodeWhale · error · anyhow::Error

OpenAI Codex OAuth credentials are unavailable. Codewhale c

Error message

OpenAI Codex OAuth credentials are unavailable.

Codewhale checks OPENAI_CODEX_ACCESS_TOKEN and CODEX_ACCESS_TOKEN automatically.
Access to the Codex CLI file is disabled by default. After `codex login`, grant read-only access explicitly with:
`codewhale auth external-consent --provider openai-codex --mode read-only --path {}`
Read-only access never refreshes or rewrites the Codex CLI file.

What it means

ApiProvider::OpenaiCodex delegates to oauth::missing_auth_message() (crates/tui/src/oauth.rs:174). Codewhale reads Codex OAuth only from OPENAI_CODEX_ACCESS_TOKEN / CODEX_ACCESS_TOKEN or, with explicit read-only consent, the Codex CLI's auth.json (path interpolated into the message). Access to that file is disabled by default and Codewhale never refreshes or rewrites it, so an expired token must be refreshed by `codex login`.

Source

Thrown at crates/tui/src/config.rs:6409

                anyhow::bail!(
                    "Moonshot/Kimi API key not found. Get a key: {}. Run 'codewhale auth set --provider moonshot', \
                     set {}, or add [{}] api_key. \
                     For a Kimi Code plan key, set [providers.moonshot] base_url = \
                     \"https://api.kimi.com/coding/v1\" and model = \"kimi-for-coding\".",
                    credential_help
                        .credential_url
                        .unwrap_or("https://platform.kimi.ai/console/api-keys"),
                    provider.env_vars_label(),
                    provider_config_table_name(provider)?
                );
            }
            ApiProvider::Anthropic | ApiProvider::Openmodel => {
                anyhow::bail!("{}", missing_provider_api_key_message(provider)?)
            }
            ApiProvider::OpencodeZen => {
                anyhow::bail!("{}", missing_provider_api_key_message(provider)?)
            }
            ApiProvider::OpenaiCodex => anyhow::bail!("{}", crate::oauth::missing_auth_message()),
            ApiProvider::Xai => {
                // Prefer OAuth guidance when auth_mode requests it or Grok CLI
                // tokens already exist; otherwise show both API-key and OAuth.
                if self
                    .provider_config_for(provider)
                    .is_some_and(provider_config_uses_xai_oauth)
                    || crate::xai_oauth::credentials_present(self)
                {
                    anyhow::bail!("{}", crate::xai_oauth::missing_auth_message());
                }
                anyhow::bail!(
                    "xAI API key not found. Get a key: https://console.x.ai/\n\
                     Run 'codewhale auth set --provider xai', set XAI_API_KEY, or add \
                     [providers.xai] api_key.\n\
                     OAuth alternative: run `codewhale auth xai-device` for \
                     Codewhale-owned storage and set [providers.xai] auth_mode = \"oauth\"."
                );
            }

View on GitHub (pinned to 8880682c63)

Solutions

  1. Provide a token via OPENAI_CODEX_ACCESS_TOKEN or CODEX_ACCESS_TOKEN for this process.
  2. Or after `codex login`, grant read-only access: codewhale auth external-consent --provider openai-codex --mode read-only --path <auth.json path from the message>.
  3. If the token is expired, run `codex login` again — Codewhale never refreshes the external file.

Example fix

# before: codex login done, but no consent, no env token
provider = "openai-codex"

# after (terminal)
# codewhale auth external-consent --provider openai-codex --mode read-only --path ~/.codex/auth.json
# or: export OPENAI_CODEX_ACCESS_TOKEN=...
Defensive patterns

Strategy: fallback

Validate before calling

fn codex_token_available() -> bool {
    ["OPENAI_CODEX_ACCESS_TOKEN", "CODEX_ACCESS_TOKEN"]
        .iter()
        .any(|v| std::env::var(v).map(|s| !s.trim().is_empty()).unwrap_or(false))
}

if !codex_token_available() {
    // plan B: prompt for external-consent on the Codex CLI auth.json,
    // or deselect openai-codex before dispatch
}

Type guard

fn codex_oauth_ready(config: &Config) -> bool {
    codex_token_available()
        || crate::oauth::stored_credentials_present_if_consented(config) // consent + unexpired token
}

Try / catch

match config.deepseek_api_key() {
    Err(e) if e.to_string().starts_with("OpenAI Codex OAuth credentials are unavailable") => {
        // fall back: another provider route, or surface the external-consent command
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: provider = openai-codex with neither env token set, no Codewhale-owned OAuth storage, and no read-only consent for the Codex CLI auth.json; or consent granted but the underlying token expired (a sibling error points back to `codex login`).

Common situations: Users assuming `codex login` alone is enough (consent is a separate step); CI without the env tokens; rotating machines where auth.json exists but was never consented.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/dd243e011dcb3c60. Report an issue: GitHub.