Compare commits

...

1 Commits

3 changed files with 16 additions and 0 deletions

View File

@@ -0,0 +1 @@
- **fix(classify429):** add missing `have exhausted their quota` pattern so the synthetic 429 from auth.ts is recognized as quota exhaustion, preventing the combo loop from burning retries against the same provider instead of falling back to a healthy one ([#9269](https://github.com/diegosouzapw/OmniRoute/issues/9269))

View File

@@ -66,6 +66,14 @@ const QUOTA_PATTERNS: ReadonlyArray<RegExp> = [
// the 429 is misclassified as transient rate_limit and retried every
// ~60s against a budget that only resets at UTC midnight.
/daily free allocation/i,
// OmniRoute auth-layer synthetic 429 (Issue #9269).
// Body: "All antigravity accounts have exhausted their quota (reset after 5m)"
// Produced by auth.ts line 1477 when every account for a provider has
// exhausted its quota. Without this pattern, the message is classified as
// a transient rate-limit and the combo loop burns retries against the
// same provider instead of falling back to a healthy one.
/have exhausted their quota/i,
];
/**

View File

@@ -44,6 +44,13 @@ test("classify429: Antigravity 'Individual quota reached' body returns 'quota_ex
assert.equal(classify429({ status: 429, body: { error: { message: body } } }), "quota_exhausted");
});
test("classify429: auth-layer synthetic 'have exhausted their quota' returns 'quota_exhausted' (#9269)", () => {
const body = "All antigravity accounts have exhausted their quota (reset after 5m)";
assert.equal(looksLikeQuotaExhausted(body), true);
assert.equal(classify429({ status: 429, body }), "quota_exhausted");
assert.equal(classify429({ status: 429, body: { error: { message: body } } }), "quota_exhausted");
});
test("classify429: Google RESOURCE_EXHAUSTED with a billing-period reset is quota exhausted", () => {
const body = "Resource has been exhausted (e.g. check quota). (reset after 24h)";
assert.equal(looksLikeQuotaExhausted(body), true);