{"record":{"id":"dd243e011dcb3c60","repo":"Hmbown/CodeWhale","slug":"openai-codex-oauth-credentials-are-unavailable-c","errorCode":null,"errorMessage":"OpenAI Codex OAuth credentials are unavailable.\n\nCodewhale checks OPENAI_CODEX_ACCESS_TOKEN and CODEX_ACCESS_TOKEN automatically.\nAccess to the Codex CLI file is disabled by default. After `codex login`, grant read-only access explicitly with:\n`codewhale auth external-consent --provider openai-codex --mode read-only --path {}`\nRead-only access never refreshes or rewrites the Codex CLI file.","messagePattern":"OpenAI Codex OAuth credentials are unavailable\\.\n\nCodewhale checks OPENAI_CODEX_ACCESS_TOKEN and CODEX_ACCESS_TOKEN automatically\\.\nAccess to the Codex CLI file is disabled by default\\. After `codex login`, grant read-only access explicitly with:\n`codewhale auth external-consent --provider openai-codex --mode read-only --path (.+?)`\nRead-only access never refreshes or rewrites the Codex CLI file\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":6715,"sourceCode":"                anyhow::bail!(\n                    \"Moonshot/Kimi API key not found. Get a key: {}. Run 'codewhale auth set --provider moonshot', \\\n                     set {}, or add [{}] api_key. \\\n                     For a Kimi Code plan key, set [providers.moonshot] base_url = \\\n                     \\\"https://api.kimi.com/coding/v1\\\" and model = \\\"kimi-for-coding\\\".\",\n                    credential_help\n                        .credential_url\n                        .unwrap_or(\"https://platform.kimi.ai/console/api-keys\"),\n                    provider.env_vars_label(),\n                    provider_config_table_name(provider)?\n                );\n            }\n            ApiProvider::Anthropic | ApiProvider::Openmodel => {\n                anyhow::bail!(\"{}\", missing_provider_api_key_message(provider)?)\n            }\n            ApiProvider::OpencodeZen => {\n                anyhow::bail!(\"{}\", missing_provider_api_key_message(provider)?)\n            }\n            ApiProvider::OpenaiCodex => anyhow::bail!(\"{}\", crate::oauth::missing_auth_message()),\n            ApiProvider::Xai => {\n                // Prefer OAuth guidance when auth_mode requests it or Grok CLI\n                // tokens already exist; otherwise show both API-key and OAuth.\n                if self\n                    .provider_config_for(provider)\n                    .is_some_and(provider_config_uses_xai_oauth)\n                    || crate::xai_oauth::credentials_present(self)\n                {\n                    anyhow::bail!(\"{}\", crate::xai_oauth::missing_auth_message());\n                }\n                anyhow::bail!(\n                    \"xAI API key not found. Get a key: https://console.x.ai/\\n\\\n                     Run 'codewhale auth set --provider xai', set XAI_API_KEY, or add \\\n                     [providers.xai] api_key.\\n\\\n                     OAuth alternative: run `codewhale auth xai-device` for \\\n                     Codewhale-owned storage and set [providers.xai] auth_mode = \\\"oauth\\\".\"\n                );\n            }","sourceCodeStart":6697,"sourceCodeEnd":6733,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/tui/src/config.rs#L6697-L6733","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Export OPENAI_CODEX_ACCESS_TOKEN (or CODEX_ACCESS_TOKEN) for this process","Run 'codex login' once, then grant read-only access: codewhale auth external-consent --provider openai-codex --mode read-only --path <auth-file>","Verify the consent path matches the Codex CLI auth file location printed in the error"],"exampleFix":"# before\n$ codewhale --provider openai-codex\n# error: OpenAI Codex OAuth credentials are unavailable...\n\n# after\n$ codex login\n$ codewhale auth external-consent --provider openai-codex --mode read-only --path ~/.codex/auth.json\n$ codewhale --provider openai-codex","handlingStrategy":"validation","validationCode":"// Pre-flight for the openai-codex OAuth route:\nfn codex_env_token() -> Option<String> {\n    [\"OPENAI_CODEX_ACCESS_TOKEN\", \"CODEX_ACCESS_TOKEN\"]\n        .iter()\n        .map(|v| std::env::var(v).ok())\n        .flatten()\n        .map(|s| s.trim().to_string())\n        .find(|s| !s.is_empty())\n}\n\nif provider == ApiProvider::OpenaiCodex {\n    assert!(codex_env_token().is_some() || read_only_grant_exists(\"openai-codex\"),\n        \"set OPENAI_CODEX_ACCESS_TOKEN or grant read-only consent after codex login\");\n}","typeGuard":"fn has_codex_oauth_source(env: Option<&str>, has_grant: bool) -> bool {\n    env.is_some_and(|t| !t.trim().is_empty()) || has_grant\n}","tryCatchPattern":"match crate::oauth::get_credentials(&grant) {\n    Err(e) if e.to_string().contains(\"OpenAI Codex OAuth credentials are unavailable\") => {\n        // print the external-consent command with the exact --path from the message\n    }\n    other => other?,\n}","preventionTips":["Codewhale never reads the Codex CLI file without consent - plan the explicit external-consent step after 'codex login'","Use OPENAI_CODEX_ACCESS_TOKEN in CI to avoid touching the CLI file at all","Read-only consent never refreshes tokens; an expired Codex token surfaces as a separate expiry error requiring a fresh 'codex login'"],"tags":["oauth","openai-codex","credentials","consent","env-var"],"backgroundTag":"missing-oauth-token","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}