fix: FriendliAI 403 credit exhaustion misclassified as auth_error (#13040)

* Add new error signal for depleted credits

* chore: add changelog fragment for #13040 FriendliAI credit-exhaustion 403 fix

* test(accountFallback): add FriendliAI credit-exhaustion 403 body test (#13040)

---------

Co-authored-by: fesshompa <fesshompa@yahoo.com>
This commit is contained in:
Tobias Andersen
2026-09-19 05:03:25 +02:00
committed by GitHub
parent 614ff4b60a
commit db022768df
3 changed files with 19 additions and 0 deletions

View File

@@ -0,0 +1 @@
- **fix(friendliai):** FriendliAI's free-tier credit-exhaustion 403 (`{"detail":"You've exhausted all your credits..."}`) is now classified as `QUOTA_EXHAUSTED` instead of `AUTH_ERROR`, so omniroute treats it as depleted credits rather than a credential problem ([#13040](https://github.com/diegosouzapw/OmniRoute/pull/13040)) — thanks @turbolego

View File

@@ -260,6 +260,8 @@ export const CREDITS_EXHAUSTED_SIGNALS = [
// marked credits_exhausted and keeps being re-selected on every request.
"insufficient credits",
"insufficient credit",
// FriendliAI 403 when free tier credits are depleted via adaptive rate limits
"exhausted all your credits",
];
// T11: Signals that indicate OAuth token is invalid/expired (not permanent deactivation)

View File

@@ -1196,6 +1196,22 @@ test("isCreditsExhausted returns true for actual credits-exhausted signals", ()
);
});
test("isCreditsExhausted matches FriendliAI credit-exhaustion 403 body (#13040)", () => {
// FriendliAI returns HTTP 403 with body {"detail":"You've exhausted all your
// credits..."} when free tier credits are depleted via Adaptive Rate Limits.
// Before #13040 this fell through every quota/credits check to the generic
// 403 -> AUTH_ERROR fallback; the signal below routes it to QUOTA_EXHAUSTED.
assert.equal(isCreditsExhausted("You've exhausted all your credits"), true);
assert.equal(
isCreditsExhausted('{"detail":"You\'ve exhausted all your credits"}'),
true
);
assert.equal(
isCreditsExhausted("exhausted all your credits"),
true
);
});
test("CREDITS_EXHAUSTED_SIGNALS no longer contains generic gRPC resource-exhausted patterns", () => {
// These patterns were removed because they falsely matched Gemini RPM 429 errors
assert.equal(CREDITS_EXHAUSTED_SIGNALS.includes("resource has been exhausted"), false);