From cb016ad8618fa7ad2792460df1abbf3f675e7762 Mon Sep 17 00:00:00 2001 From: bot Date: Sun, 12 Apr 2026 13:30:15 +0800 Subject: [PATCH] fix: handle Anthropic credit balance exhausted (400) as account error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an Anthropic API key's credit balance is depleted, the upstream returns HTTP 400 with message containing "credit balance". Previously, the 400 handler only checked for "organization has been disabled", so credit-exhausted accounts kept being scheduled — every request returned the same error. Treat this case identically to 402 (Payment Required): call handleAuthError → SetError to stop scheduling the account until an admin manually recovers it after topping up credits. Closes #1586 --- backend/internal/service/ratelimit_service.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/backend/internal/service/ratelimit_service.go b/backend/internal/service/ratelimit_service.go index 4f5b57cc..4d8009b7 100644 --- a/backend/internal/service/ratelimit_service.go +++ b/backend/internal/service/ratelimit_service.go @@ -142,11 +142,16 @@ func (s *RateLimitService) HandleUpstreamError(ctx context.Context, account *Acc switch statusCode { case 400: - // 只有当错误信息包含 "organization has been disabled" 时才禁用 + // "organization has been disabled" → 永久禁用 if strings.Contains(strings.ToLower(upstreamMsg), "organization has been disabled") { msg := "Organization disabled (400): " + upstreamMsg s.handleAuthError(ctx, account, msg) shouldDisable = true + } else if account.Platform == PlatformAnthropic && strings.Contains(strings.ToLower(upstreamMsg), "credit balance") { + // Anthropic API key 余额不足(语义等同 402),停止调度 + msg := "Credit balance exhausted (400): " + upstreamMsg + s.handleAuthError(ctx, account, msg) + shouldDisable = true } // 其他 400 错误(如参数问题)不处理,不禁用账号 case 401: