fix(model-fallback): add HTTP statusCode check for GLM rate limit fallback

isRetryableModelError() now checks the HTTP status code (429/503/529)
in addition to existing message pattern matching. This ensures rate
limit errors trigger model fallback regardless of error message format
or language (e.g., Chinese GLM errors).

Changes:
- ErrorInfo interface extended with statusCode?: number
- isRetryableModelError() checks statusCode after STOP patterns, before
  message pattern fallback
- extractErrorStatusCode() added to error-classifier.ts (supports
  statusCode, status, code, response.status fields)
- GLM-specific STOP patterns added: daily call limit, in arrears,
  fair use policy, recharge and try — these prevent quota/billing 429s
  from being treated as transient rate limits
- statusCode propagated through tryFallbackRetry and manager.ts

400 intentionally excluded from statusCode check (permanent client error).
This commit is contained in:
cailgarrisk-collab
2026-05-03 11:59:13 +02:00
parent 9ba3b574a7
commit 61d2f1195b
5 changed files with 187 additions and 4 deletions
+19
View File
@@ -99,6 +99,13 @@ const STOP_MESSAGE_PATTERNS = [
"credit balance",
"usage limit for this month",
"exhausted your capacity",
// GLM/Z.ai business error codes that indicate permanent quota/billing exhaustion
"daily call limit",
"daily limit",
"usage limit reached for",
"in arrears",
"fair use policy",
"recharge and try",
]
const AUTO_RETRY_GATE_PATTERNS = [
@@ -117,6 +124,8 @@ function hasProviderAutoRetrySignal(message: string): boolean {
export interface ErrorInfo {
name?: string
message?: string
/** HTTP status code from the provider response (e.g., 429 for rate limit) */
statusCode?: number
}
/**
@@ -151,6 +160,16 @@ export function isRetryableModelError(error: ErrorInfo): boolean {
if (hasProviderAutoRetrySignal(msg)) {
return true
}
// HTTP status code check: catches rate-limit errors regardless of message format/language.
// Uses the same codes as runtime-fallback config (400 excluded as it is a permanent client error).
if (
error.statusCode != null &&
(error.statusCode === 429 || error.statusCode === 503 || error.statusCode === 529)
) {
return true
}
return RETRYABLE_MESSAGE_PATTERNS.some((pattern) => msg.includes(pattern))
}