Hmbown/CodeWhale · error · anyhow::Error

OpenAI Codex OAuth credentials are unavailable. Codewhale…

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

The OpenAI Codex route resolves OAuth credentials via crate::oauth, which checks OPENAI_CODEX_ACCESS_TOKEN and CODEX_ACCESS_TOKEN automatically, and reads the Codex CLI auth file only under an explicit read-only consent grant (disabled by default). When none of these yield credentials, get_credentials attaches missing_auth_message (crates/tui/src/oauth.rs:174) as context and the provider arm bails with it. The message stresses that read-only consent never refreshes or rewrites the Codex CLI file.

Solutions

  1. Export OPENAI_CODEX_ACCESS_TOKEN (or CODEX_ACCESS_TOKEN) for this process
  2. Run 'codex login' once, then grant read-only access: codewhale auth external-consent --provider openai-codex --mode read-only --path <auth-file>
  3. Verify the consent path matches the Codex CLI auth file location printed in the error

Example fix

# before
$ codewhale --provider openai-codex
# error: OpenAI Codex OAuth credentials are unavailable...

# after
$ codex login
$ codewhale auth external-consent --provider openai-codex --mode read-only --path ~/.codex/auth.json
$ codewhale --provider openai-codex
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight for the openai-codex OAuth route:
fn codex_env_token() -> Option<String> {
    ["OPENAI_CODEX_ACCESS_TOKEN", "CODEX_ACCESS_TOKEN"]
        .iter()
        .map(|v| std::env::var(v).ok())
        .flatten()
        .map(|s| s.trim().to_string())
        .find(|s| !s.is_empty())
}

if provider == ApiProvider::OpenaiCodex {
    assert!(codex_env_token().is_some() || read_only_grant_exists("openai-codex"),
        "set OPENAI_CODEX_ACCESS_TOKEN or grant read-only consent after codex login");
}

Type guard

fn has_codex_oauth_source(env: Option<&str>, has_grant: bool) -> bool {
    env.is_some_and(|t| !t.trim().is_empty()) || has_grant
}

Try / catch

match crate::oauth::get_credentials(&grant) {
    Err(e) if e.to_string().contains("OpenAI Codex OAuth credentials are unavailable") => {
        // print the external-consent command with the exact --path from the message
    }
    other => other?,
}

Prevention

When it happens

Trigger: provider == ApiProvider::OpenaiCodex and the OpenaiCodex arm calls crate::oauth::missing_auth_message(); both env vars are unset/empty, and there is no read-only external-consent grant for the Codex CLI auth file (or the file does not exist because 'codex login' was never run).

Common situations: Expecting Codewhale to auto-import a Codex CLI login without granting consent; env vars set in a different shell; token expired is a separate later error; hardened setups where the CLI file exists but access is deliberately disabled.

Related errors


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

Appendix: source

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

                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 0c42157ee5)