From d527e9e629f0b4651c34922e2afe9bd9dd2fbae8 Mon Sep 17 00:00:00 2001 From: sunnysab Date: Wed, 28 Oct 2026 14:43:32 +0800 Subject: [PATCH] fix(gateway): classify "insufficient credits" as a missing balance MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A relay that answered `400 {"code":"BAD_REQUEST"}` with "You have insufficient credits to make this request." matched none of the `NO_BALANCE` phrases: the list knew "insufficient balance" and "credit balance is too low", not "insufficient credits". The verdict fell through to `ClientError`, so the request went back to the client, the upstream was never swapped and never paused — the client retried the same dead upstream until the operator disabled it by hand. Add the credit phrasings, including "don't have enough credits" (the contracting variant a phrase starting with "not enough" misses). --- crates/tw-gateway/src/failure.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/tw-gateway/src/failure.rs b/crates/tw-gateway/src/failure.rs index 6d33d0d1..1446476a 100644 --- a/crates/tw-gateway/src/failure.rs +++ b/crates/tw-gateway/src/failure.rs @@ -85,6 +85,9 @@ const NO_BALANCE: &[&str] = &[ "insufficient account balance", "credit balance is too low", "balance is too low", + "insufficient credit", + "not enough credit", + "have enough credit", "payment required", "billing_hard_limit", "arrearage", @@ -236,6 +239,14 @@ mod tests { r#"{"error":{"code":"insufficient_quota","message":"You exceeded your current quota"}}"#, ), (402, r#"{"error":{"message":"Insufficient Balance"}}"#), + ( + 400, + r#"{"message":"You have insufficient credits to make this request. Please purchase more credits to continue using the service.","type":"invalid_request_error","code":"BAD_REQUEST"}"#, + ), + ( + 400, + r#"{"error":{"message":"You don't have enough credits for this model."}}"#, + ), (403, r#"{"error":{"code":"Arrearage"}}"#), ( 429,