Document permanent refresh failure

This commit is contained in:
Eric Traut
2026-03-10 16:14:59 -06:00
parent d9d6129a11
commit b8951c97ce
2 changed files with 37 additions and 55 deletions

View File

@@ -1366,29 +1366,30 @@ impl CodexMessageProcessor {
}
} else {
let refresh_failure = self.auth_manager.refresh_failure();
let permanent_refresh_failure = refresh_failure.is_some()
|| matches!(
refresh_outcome,
RefreshTokenRequestOutcome::FailedPermanently
);
match self.auth_manager.auth().await {
Some(auth) => {
let auth_mode = auth.api_auth_mode();
let (reported_auth_method, token_opt) = if include_token
&& (refresh_failure.is_some()
|| matches!(
refresh_outcome,
RefreshTokenRequestOutcome::FailedPermanently
)) {
(Some(auth_mode), None)
} else {
match auth.get_token() {
Ok(token) if !token.is_empty() => {
let tok = if include_token { Some(token) } else { None };
(Some(auth_mode), tok)
let (reported_auth_method, token_opt) =
if include_token && permanent_refresh_failure {
(Some(auth_mode), None)
} else {
match auth.get_token() {
Ok(token) if !token.is_empty() => {
let tok = if include_token { Some(token) } else { None };
(Some(auth_mode), tok)
}
Ok(_) => (None, None),
Err(err) => {
tracing::warn!("failed to get token for auth status: {err}");
(None, None)
}
}
Ok(_) => (None, None),
Err(err) => {
tracing::warn!("failed to get token for auth status: {err}");
(None, None)
}
}
};
};
GetAuthStatusResponse {
auth_method: reported_auth_method,
auth_token: token_opt,

View File

@@ -815,15 +815,9 @@ struct CachedAuth {
auth: Option<CodexAuth>,
/// Callback used to refresh external auth by asking the parent app for new tokens.
external_refresher: Option<Arc<dyn ExternalAuthRefresher>>,
/// Permanent refresh failure cached for the current managed auth snapshot so
/// Permanent refresh failure cached for the current auth snapshot so
/// later refresh attempts for the same credentials fail fast without network.
permanent_refresh_failure: Option<PermanentRefreshFailure>,
}
#[derive(Clone, Debug)]
struct PermanentRefreshFailure {
auth_dot_json: AuthDotJson,
error: RefreshTokenFailedError,
permanent_refresh_failure: Option<RefreshTokenFailedError>,
}
impl Debug for CachedAuth {
@@ -842,7 +836,7 @@ impl Debug for CachedAuth {
&self
.permanent_refresh_failure
.as_ref()
.map(|failure| failure.error.reason),
.map(|failure| failure.reason),
)
.finish()
}
@@ -1142,39 +1136,34 @@ impl AuthManager {
}
}
/// Returns the cached permanent refresh failure only when `auth` still
/// matches the current cached auth snapshot.
fn permanent_refresh_failure_for_auth(
&self,
auth: &CodexAuth,
) -> Option<RefreshTokenFailedError> {
let auth_dot_json = auth.get_current_auth_json()?;
self.inner
.read()
.ok()
.and_then(|cached| cached.permanent_refresh_failure.clone())
.filter(|failure| failure.auth_dot_json == auth_dot_json)
.map(|failure| failure.error)
.filter(|_| {
let current_auth = self.auth_cached();
Self::auths_equal_for_refresh(Some(auth), current_auth.as_ref())
})
}
/// Records a permanent refresh failure only if the failed refresh was
/// attempted against the auth snapshot that is still cached.
fn record_permanent_refresh_failure_if_unchanged(
&self,
attempted_auth: &CodexAuth,
error: &RefreshTokenFailedError,
) {
let Some(attempted_auth_dot_json) = attempted_auth.get_current_auth_json() else {
return;
};
if let Ok(mut guard) = self.inner.write() {
let current_auth_matches = guard
.auth
.as_ref()
.and_then(CodexAuth::get_current_auth_json)
.is_some_and(|current| current == attempted_auth_dot_json);
let current_auth_matches =
Self::auths_equal_for_refresh(Some(attempted_auth), guard.auth.as_ref());
if current_auth_matches {
guard.permanent_refresh_failure = Some(PermanentRefreshFailure {
auth_dot_json: attempted_auth_dot_json,
error: error.clone(),
});
guard.permanent_refresh_failure = Some(error.clone());
}
}
}
@@ -1193,17 +1182,9 @@ impl AuthManager {
if let Ok(mut guard) = self.inner.write() {
let previous = guard.auth.as_ref();
let changed = !AuthManager::auths_equal(previous, new_auth.as_ref());
let permanent_refresh_failure_still_matches = guard
.permanent_refresh_failure
.as_ref()
.and_then(|failure| {
new_auth
.as_ref()
.and_then(CodexAuth::get_current_auth_json)
.map(|current| current == failure.auth_dot_json)
})
.unwrap_or(false);
if !permanent_refresh_failure_still_matches {
let auth_changed_for_refresh =
!Self::auths_equal_for_refresh(previous, new_auth.as_ref());
if auth_changed_for_refresh {
guard.permanent_refresh_failure = None;
}
tracing::info!("Reloaded auth, changed: {changed}");