{"record":{"id":"6285e5cd8d30db3b","repo":"Hmbown/CodeWhale","slug":"openai-codex-uses-oauth-sign-in-with-chatgpt-via-codewhale","errorCode":null,"errorMessage":"OpenAI Codex uses OAuth. Sign in with ChatGPT via `codewhale auth chatgpt` (subscription billing, Codewhale-owned tokens). The openai API-key route is a different billing owner. Alternatively run `codex login`, then grant exact read-only access with `codewhale auth external-consent --provider openai-codex --mode read-only`, or set OPENAI_CODEX_ACCESS_TOKEN for this process; Codewhale does not store an API key for this provider.","messagePattern":"OpenAI Codex uses OAuth\\. Sign in with ChatGPT via `codewhale auth chatgpt` \\(subscription billing, Codewhale-owned tokens\\)\\. The openai API-key route is a different billing owner\\. Alternatively run `codex login`, then grant exact read-only access with `codewhale auth external-consent --provider openai-codex --mode read-only`, or set OPENAI_CODEX_ACCESS_TOKEN for this process; Codewhale does not store an API key for this provider\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/tui/src/config.rs","lineNumber":12502,"sourceCode":"    route_config: &Config,\n    api_key: &str,\n) -> Result<SavedCredential> {\n    if identity.provider == ApiProvider::Xai {\n        return codewhale_config::with_xai_oauth_revocation_transaction(|| {\n            save_api_key_for_identity_unlocked(identity, route_config, api_key)\n        });\n    }\n    save_api_key_for_identity_unlocked(identity, route_config, api_key)\n}\n\nfn save_api_key_for_identity_unlocked(\n    identity: &ProviderIdentity,\n    route_config: &Config,\n    api_key: &str,\n) -> Result<SavedCredential> {\n    let provider = identity.provider;\n    if provider == ApiProvider::OpenaiCodex {\n        anyhow::bail!(\n            \"OpenAI Codex uses OAuth. Sign in with ChatGPT via `codewhale auth chatgpt` (subscription billing, Codewhale-owned tokens). The openai API-key route is a different billing owner. Alternatively run `codex login`, then grant exact read-only access with `codewhale auth external-consent --provider openai-codex --mode read-only`, or set OPENAI_CODEX_ACCESS_TOKEN for this process; Codewhale does not store an API key for this provider.\"\n        );\n    }\n    let is_legacy_literal_custom = provider == ApiProvider::Custom\n        && identity.key.trim() == ApiProvider::Custom.as_str()\n        && identity.persisted_id().is_none();\n    if matches!(provider, ApiProvider::Deepseek | ApiProvider::DeepseekCN) {\n        return save_api_key(api_key);\n    }\n    if is_legacy_literal_custom {\n        return save_root_api_key_for_secret_slot(api_key, \"custom\", false);\n    }\n\n    let api_key = api_key.trim();\n    anyhow::ensure!(!api_key.is_empty(), \"Refusing to save an empty API key.\");\n\n    let config_path =\n        credential_config_path().context(\"Failed to resolve config path for provider API key.\")?;","sourceCodeStart":12484,"sourceCodeEnd":12520,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/config.rs#L12484-L12520","documentation":"The credential-save path refuses to store an API key for the `openai-codex` provider because Codex is OAuth-only: billing runs through a ChatGPT subscription with Codewhale-owned tokens, not a user API key. The error explains the supported sign-in routes instead of persisting a key that the provider would never use.","triggerScenarios":"Calling the provider credential save routine with `identity.provider == ApiProvider::OpenaiCodex` and any API key.","commonSituations":"User pastes an OpenAI platform API key expecting it to work with Codex; scripts that generically save keys for every provider; migrating configs from the plain `openai` provider to `openai-codex`.","solutions":["Run `codewhale auth chatgpt` to sign in with ChatGPT OAuth (subscription billing)","Or run `codex login`, then `codewhale auth external-consent --provider openai-codex --mode read-only`","Or set the `OPENAI_CODEX_ACCESS_TOKEN` env var for this process only (Codewhale does not store it)"],"exampleFix":"// before\ncodewhale auth save --provider openai-codex --api-key sk-...\n// after\ncodewhale auth chatgpt","handlingStrategy":"try-catch","validationCode":"if provider == ApiProvider::OpenaiCodex {\n    eprintln!(\"openai-codex is OAuth-only; use `codewhale auth chatgpt`\");\n} else { save_api_key(...)?; }","typeGuard":"fn supports_api_key(p: ApiProvider) -> bool { p != ApiProvider::OpenaiCodex }","tryCatchPattern":"match save_result {\n    Err(e) if e.to_string().starts_with(\"OpenAI Codex uses OAuth\") => run_chatgpt_login_flow(),\n    other => other?,\n}","preventionTips":["Never attempt to store API keys for OAuth-only providers","Route openai-codex users straight to the ChatGPT login flow","Check provider metadata before generic key-save automation"],"tags":["oauth","authentication","openai","provider"],"backgroundTag":"authentication-required","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}