mirror of
https://github.com/openai/codex.git
synced 2026-11-07 20:30:33 +00:00
Stop retrying terminal runtime authentication denials (#60096)
## Why Invalid assertions and paused or revoked runtime instances cannot recover through request retries or credential refresh. Permanent refresh-token failures also need to retain their sign-in error instead of becoming retryable stream errors. ## What changed - Recognize terminal runtime denial codes in `401` and `403` response bodies and identity error headers, preserving HTTP diagnostics while stopping retries. - Skip credential recovery for runtime denials in runtime-enabled model providers and backend requests, while preserving recovery for ordinary credential expiration. - Propagate permanent refresh-token failures through runtime authentication as `RefreshTokenFailed` errors. ## Testing Add coverage for body and header denials, preserved HTTP diagnostics, provider-scoped credential recovery, and cached permanent refresh failures. Complete-turn tests verify that runtime denials stop without retries or refreshes while expired credentials recover in both runtime authentication modes. GitOrigin-RevId: 33f6f707454130e97e13f3b53e29d2c484602152
This commit is contained in:
@@ -183,6 +183,7 @@ impl From<RuntimeAuthError> for AuthError {
|
||||
| RuntimeAuthError::UnsupportedResourceSelection
|
||||
| RuntimeAuthError::Changed
|
||||
| RuntimeAuthError::RefreshFailed
|
||||
| RuntimeAuthError::RefreshTokenFailed(_)
|
||||
| RuntimeAuthError::KeyGeneration
|
||||
| RuntimeAuthError::Registration(
|
||||
RegistrationError::InvalidEndpoint
|
||||
|
||||
@@ -17,6 +17,7 @@ use codex_login::AuthManager;
|
||||
use codex_login::AuthOwner;
|
||||
use codex_login::CodexAuth;
|
||||
use codex_login::RefreshTokenError;
|
||||
use codex_login::auth::RefreshTokenFailedError;
|
||||
use http::StatusCode;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::Mutex;
|
||||
@@ -169,9 +170,8 @@ impl RuntimeAuthBinding {
|
||||
self.check_source(expected_source)?;
|
||||
recovery.next().await.map_err(|error| match error {
|
||||
RefreshTokenError::Policy(denied) => RuntimeAuthError::Policy(denied),
|
||||
RefreshTokenError::Permanent(_) | RefreshTokenError::Transient(_) => {
|
||||
RuntimeAuthError::RefreshFailed
|
||||
}
|
||||
RefreshTokenError::Permanent(error) => RuntimeAuthError::RefreshTokenFailed(error),
|
||||
RefreshTokenError::Transient(_) => RuntimeAuthError::RefreshFailed,
|
||||
})?;
|
||||
let (auth, http_client_factory) = self
|
||||
.credentials
|
||||
@@ -340,8 +340,8 @@ impl RuntimeAuthBinding {
|
||||
.credentials
|
||||
.auth_change_state_for(auth)
|
||||
.ok_or(RuntimeAuthError::Changed)?;
|
||||
if self.credentials.refresh_failure_for_auth(auth).is_some() {
|
||||
return Err(RuntimeAuthError::RefreshFailed);
|
||||
if let Some(error) = self.credentials.refresh_failure_for_auth(auth) {
|
||||
return Err(RuntimeAuthError::RefreshTokenFailed(error));
|
||||
}
|
||||
Ok(state)
|
||||
}
|
||||
@@ -390,6 +390,8 @@ pub enum RuntimeAuthError {
|
||||
Changed,
|
||||
#[error("runtime registration could not refresh actor credentials")]
|
||||
RefreshFailed,
|
||||
#[error(transparent)]
|
||||
RefreshTokenFailed(#[from] RefreshTokenFailedError),
|
||||
#[error("failed to generate runtime signing key")]
|
||||
KeyGeneration,
|
||||
#[error(transparent)]
|
||||
|
||||
Reference in New Issue
Block a user