gateway: a 429 that says the balance is spent is out of credit, not a rate limit (kkgg on Discord: GLM 直接429了)

Zhipu answers a GLM Coding Plan's key sent to its pay-as-you-go endpoint with 429 and 1113 "余额不足或无可用资源包,请充值". failure() left every 429 out of the credit words (so "Add 10 credits to unlock 1000 free requests per day" stays a used-up plan), and the request list showed "429 · 限流", which sent the user looking for a rate limit. A 429 that names an insufficient balance (insufficient balance/credit/funds, 余额不足, 欠费, 请充值) is failCredit now: the list says "429 · 余额不足", and the key rests as one out of credit.

TestRateLimitIsNotQuota has Zhipu's body as it comes and as magpie words it (rate_limit_error), in Chinese and English, and fails without the change. go vet, go build (GOOS=linux nogui, GOOS=windows) and go test -tags nogui ./... under a temp HOME pass.
This commit is contained in:
yetone committed 2026-10-06 01:28:58 +08:00
1 parent 7a926dd3ce
commit e63fbb0e69
2 files changed
+10 -1

No files matched your search

+5
View File
@@ -17,6 +17,11 @@ func TestRateLimitIsNotQuota(t *testing.T) {
`{"error":{"type":"usage_limit_reached","message":"You've hit your usage limit."}}`: failQuota,
`{"error":{"message":"Resource has been exhausted (e.g. check quota).","status":"RESOURCE_EXHAUSTED"}}`: failQuota,
`{"error":{"message":"You exceeded your current quota","type":"insufficient_quota"}}`: failCredit,
// Zhipu's pay-as-you-go endpoint answering a Coding Plan's key
// (kkgg on Discord: shown as "429 · rate limited")
`{"error":{"code":"1113","message":"余额不足或无可用资源包,请充值。"}}`: failCredit,
`{"error":{"message":"Zhipu GLM: 余额不足或无可用资源包,请充值。","type":"rate_limit_error"}}`: failCredit,
`{"error":{"code":"1113","message":"Insufficient balance or no resource package. Please recharge."}}`: failCredit,
} {
if got := failure(429, []byte(body)); got != want {
t.Errorf("%s: %s, want %s", body, got, want)
+5 -1
View File
@@ -108,6 +108,10 @@ const (
var (
// creditWords: the account or key has no money left.
creditWords = regexp.MustCompile(`(?i)insufficient.?(balance|credit|fund)|balance|credit|billing|payment|arrear|overdue|suspended|余额|欠费|充值|账户.*(不足|停)`)
// brokeWords: a 429 that says the balance itself is spent, as Zhipu
// answers a GLM Coding Plan's key at the pay-as-you-go endpoint (1113
// "余额不足或无可用资源包,请充值") — out of credit, not a rate limit
brokeWords = regexp.MustCompile(`(?i)insufficient.?(balance|credit|fund)|余额不足|欠费|请充值`)
// quotaWords: it has used up what its plan allows for now.
usedUpWords = regexp.MustCompile(`(?i)quota|usage.?limit|out of budget|budget (exceeded|exhausted)|limit.?reached|hit your .*limit|limit.{0,24}resets|exceeded.*(plan|limit)|额度|用量|套餐|上限`)
// rateWords: a 429 that is a short rate limit — requests or tokens per
@@ -188,7 +192,7 @@ func failure(status int, body []byte) string {
// Kimi's "exceeded model token limit" is the conversation's
// length, not the plan's
return failOther
case status == 402, creditWords.Match(body) && status != 429 || strings.Contains(string(body), "insufficient_quota"):
case status == 402, creditWords.Match(body) && (status != 429 || brokeWords.Match(body)) || strings.Contains(string(body), "insufficient_quota"):
return failCredit
case status == 429 && rateWords.Match(body) && !plannedWords.Match(body):
return failRate