fix(auth-profiles): never shorten cooldown deadline on retry

When the backoff saturates at 60 min and retries fire every 30 min
(e.g. cron jobs), each failed request was resetting cooldownUntil to
now+60m.  Because now+60m < existing deadline, the window kept getting
renewed and the profile never recovered without manually clearing
usageStats in auth-profiles.json.

Fix: only write a new cooldownUntil (or disabledUntil for billing) when
the new deadline is strictly later than the existing one.  This lets the
original window expire naturally while still allowing genuine backoff
extension when error counts climb further.

Fixes #23516

[AI-assisted]
This commit is contained in:
artale
2026-02-22 12:53:44 +01:00
committed by Peter Steinberger
parent bec059f7b2
commit dc69610d51
3 changed files with 114 additions and 2 deletions

View File

@@ -287,11 +287,25 @@ function computeNextProfileUsageStats(params: {
baseMs: params.cfgResolved.billingBackoffMs,
maxMs: params.cfgResolved.billingMaxMs,
});
updatedStats.disabledUntil = params.now + backoffMs;
const newDisabledUntil = params.now + backoffMs;
// Only advance disabledUntil — never shorten an existing window.
// A retry that fires while the profile is already disabled must not reset
// the deadline to an earlier time; it may extend it if the new backoff is longer.
if (!params.existing.disabledUntil || newDisabledUntil > params.existing.disabledUntil) {
updatedStats.disabledUntil = newDisabledUntil;
}
updatedStats.disabledReason = "billing";
} else {
const backoffMs = calculateAuthProfileCooldownMs(nextErrorCount);
updatedStats.cooldownUntil = params.now + backoffMs;
const newCooldownUntil = params.now + backoffMs;
// Only advance cooldownUntil — never shorten an existing window.
// When the backoff saturates (60 min) and retries fire every 30 min, each
// retry was resetting cooldownUntil to now+60m, preventing the profile from
// ever recovering. We only write a new deadline when it is strictly later
// than the one already in the store.
if (!params.existing.cooldownUntil || newCooldownUntil > params.existing.cooldownUntil) {
updatedStats.cooldownUntil = newCooldownUntil;
}
}
return updatedStats;