Handle transient API key validation failures

This commit is contained in:
Rahul Thathoo
2026-05-10 17:00:52 -07:00
parent cf1f255a5f
commit 4ec09d3030
2 changed files with 57 additions and 0 deletions
@@ -45,6 +45,10 @@ enum RefreshTokenRequestOutcome {
FailedPermanently,
}
fn api_key_validation_unavailable() -> JSONRPCErrorError {
invalid_request("Could not validate API key right now. Check your connection and try again.")
}
impl Drop for ActiveLogin {
fn drop(&mut self) {
self.cancel();
@@ -297,6 +301,17 @@ impl AccountRequestProcessor {
CodexErr::UnexpectedStatus(err) if matches!(err.status.as_u16(), 401 | 403) => {
invalid_request("API key is invalid or unusable.")
}
CodexErr::UnexpectedStatus(err)
if err.status.is_server_error() || matches!(err.status.as_u16(), 408 | 429) =>
{
api_key_validation_unavailable()
}
CodexErr::Timeout
| CodexErr::Stream(..)
| CodexErr::ResponseStreamFailed(_)
| CodexErr::ConnectionFailed(_)
| CodexErr::InternalServerError
| CodexErr::RetryLimit(_) => api_key_validation_unavailable(),
err => internal_error(format!("failed to validate api key: {err}")),
})
}
@@ -994,6 +994,48 @@ async fn login_account_api_key_rejects_unusable_key_before_persisting() -> Resul
Ok(())
}
#[tokio::test]
async fn login_account_api_key_validation_transient_failure_is_retryable() -> Result<()> {
let codex_home = TempDir::new()?;
let mock_server = MockServer::start().await;
create_config_toml(
codex_home.path(),
CreateConfigTomlParams {
requires_openai_auth: Some(true),
base_url: Some(format!("{}/v1", mock_server.uri())),
..Default::default()
},
)?;
Mock::given(method("GET"))
.and(path("/v1/models"))
.respond_with(ResponseTemplate::new(500).set_body_json(json!({
"error": { "message": "temporary outage" }
})))
.expect(1)
.mount(&mock_server)
.await;
let mut mcp = McpProcess::new(codex_home.path()).await?;
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
let request_id = mcp
.send_login_account_api_key_request("sk-test-key")
.await?;
let err: JSONRPCError = timeout(
DEFAULT_READ_TIMEOUT,
mcp.read_stream_until_error_message(RequestId::Integer(request_id)),
)
.await??;
assert_eq!(
err.error.message,
"Could not validate API key right now. Check your connection and try again."
);
assert!(!codex_home.path().join("auth.json").exists());
Ok(())
}
#[tokio::test]
async fn login_account_api_key_rejected_when_forced_chatgpt() -> Result<()> {
let codex_home = TempDir::new()?;