Problem Statement
When the upstream provider permanently revokes an account's OAuth token (e.g. the one-time refresh token was consumed elsewhere — the scenario behind #495-style incidents), the runtime handles it with a cooldown: applyMonotonicAuthCooldown(tokenInvalidationCooldownMs) (default 5 min) plus recordFailure (lib/runtime-rotation-proxy.ts, upstream-401 invalidation path). Removal only happens after MAX_AUTH_FAILURES_BEFORE_REMOVAL = 3 consecutive auth failures (lib/request/failure-policy.ts, lib/constants.ts).
Between revocation and eventual removal, the operator has no persisted signal that the account is dead:
status / list show the account as normal (or just generic [rate-limited] flags); only a live check probe reveals "authentication token has been invalidated".
- Selection diagnostics show
health=100 for the revoked account.
- Nothing marks "this account needs re-login" until the account silently disappears via the 3-strike removal.
In our incident we only understood the state after running live probes per account, and worked around it by manually account pause-ing the revoked account.
Proposed Solution
Persist an invalidation marker on the account when the invalidation path fires — e.g. authInvalidatedAt (+ optional last error code) stored alongside consecutiveAuthFailures:
- cleared on the next successful refresh or login;
- rendered as a badge in
list / status / check (e.g. [token-invalid — re-login needed]);
- exposed to selection diagnostics (
why-selected) so the account shows as unavailable-with-reason instead of health=100;
- optionally, while the marker is set, selection treats the account like
paused (candidate-excluded) instead of retrying it every cooldown expiry — this removes the 401-flap loop for permanently revoked tokens while keeping the anti-cascade cooldown rationale intact.
Expected impact: operators can see at a glance which account needs re-login, and revoked accounts stop periodically re-entering selection until a human fixes them.
Alternatives Considered
- Rely on the 3-strike auto-removal alone: works eventually, but the removal is silent, the pre-removal window is invisible, and removal deletes the stored account (heavier UX than "flagged, waiting for re-login").
- Longer
tokenInvalidationCooldownMs: reduces flapping but still invisible to the operator and delays legitimate recovery after a re-login.
- Manual
account pause (our current workaround): effective but requires the operator to have already diagnosed the problem.
Scope
In scope: persisting/clearing the marker on the existing invalidation code paths, surfacing it in list/status/check/why-selected, optional candidate exclusion while set. Out of scope: any change to OAuth flows, refresh lease behavior, or the 3-strike removal policy itself.
Validation Plan
- Unit: simulate the upstream-401 invalidation path → marker persisted; successful refresh/login → marker cleared.
- Command-level: with a revoked account,
codex-multi-auth list and status show the badge without a live probe; why-selected shows the account as unavailable with reason; after login, badge clears and the account rejoins selection.
Compliance Confirmation
Problem Statement
When the upstream provider permanently revokes an account's OAuth token (e.g. the one-time refresh token was consumed elsewhere — the scenario behind #495-style incidents), the runtime handles it with a cooldown:
applyMonotonicAuthCooldown(tokenInvalidationCooldownMs)(default 5 min) plusrecordFailure(lib/runtime-rotation-proxy.ts, upstream-401 invalidation path). Removal only happens afterMAX_AUTH_FAILURES_BEFORE_REMOVAL = 3consecutive auth failures (lib/request/failure-policy.ts,lib/constants.ts).Between revocation and eventual removal, the operator has no persisted signal that the account is dead:
status/listshow the account as normal (or just generic[rate-limited]flags); only a livecheckprobe reveals "authentication token has been invalidated".health=100for the revoked account.In our incident we only understood the state after running live probes per account, and worked around it by manually
account pause-ing the revoked account.Proposed Solution
Persist an invalidation marker on the account when the invalidation path fires — e.g.
authInvalidatedAt(+ optional last error code) stored alongsideconsecutiveAuthFailures:list/status/check(e.g.[token-invalid — re-login needed]);why-selected) so the account shows as unavailable-with-reason instead ofhealth=100;paused(candidate-excluded) instead of retrying it every cooldown expiry — this removes the 401-flap loop for permanently revoked tokens while keeping the anti-cascade cooldown rationale intact.Expected impact: operators can see at a glance which account needs re-login, and revoked accounts stop periodically re-entering selection until a human fixes them.
Alternatives Considered
tokenInvalidationCooldownMs: reduces flapping but still invisible to the operator and delays legitimate recovery after a re-login.account pause(our current workaround): effective but requires the operator to have already diagnosed the problem.Scope
In scope: persisting/clearing the marker on the existing invalidation code paths, surfacing it in list/status/check/why-selected, optional candidate exclusion while set. Out of scope: any change to OAuth flows, refresh lease behavior, or the 3-strike removal policy itself.
Validation Plan
codex-multi-auth listandstatusshow the badge without a live probe;why-selectedshows the account as unavailable with reason; afterlogin, badge clears and the account rejoins selection.Compliance Confirmation