Skip to content

Commit 19b3094

Browse files
committed
feat(detection): expand error patterns for gRPC, 402/529, and credit errors
Add ratelimit (one word), resource exhausted (Google gRPC RESOURCE_EXHAUSTED), HTTP 402 (quota_exceeded), HTTP 529 (overloaded), and insufficient credit(s) patterns. Covers real-world error shapes from Anthropic, OpenAI, and Google providers.
1 parent 4851f47 commit 19b3094

File tree

3 files changed

+54
-3
lines changed

3 files changed

+54
-3
lines changed

src/config/defaults.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,16 @@ import type { PluginConfig } from "../types.js";
44

55
export const DEFAULT_PATTERNS = [
66
"rate limit",
7+
"ratelimit",
78
"usage limit",
89
"too many requests",
910
"quota exceeded",
1011
"overloaded",
1112
"capacity exceeded",
1213
"credits exhausted",
14+
"insufficient credit",
1315
"billing limit",
16+
"resource exhausted",
1417
"429",
1518
];
1619

src/detection/classifier.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
11
import type { ErrorCategory } from "../types.js";
22
import { matchesAnyPattern } from "./patterns.js";
33

4-
const RATE_LIMIT_PATTERNS = ["rate limit", "ratelimit", "too many requests", "usage limit", "429"];
4+
const RATE_LIMIT_PATTERNS = [
5+
"rate limit",
6+
"ratelimit",
7+
"too many requests",
8+
"usage limit",
9+
"resource exhausted",
10+
"resource_exhausted",
11+
"429",
12+
];
513

614
const QUOTA_PATTERNS = [
715
"quota exceeded",
816
"credits exhausted",
917
"billing limit",
1018
"credit limit",
1119
"insufficient quota",
20+
"insufficient credit",
21+
"insufficient credits",
1222
"out of credits",
1323
];
1424

@@ -38,10 +48,10 @@ export function classifyError(message: string, statusCode?: number): ErrorCatego
3848
if (statusCode === 429 || matchesAnyPattern(text, RATE_LIMIT_PATTERNS)) {
3949
return "rate_limit";
4050
}
41-
if (matchesAnyPattern(text, QUOTA_PATTERNS)) {
51+
if (statusCode === 402 || matchesAnyPattern(text, QUOTA_PATTERNS)) {
4252
return "quota_exceeded";
4353
}
44-
if (matchesAnyPattern(text, OVERLOADED_PATTERNS)) {
54+
if (statusCode === 529 || matchesAnyPattern(text, OVERLOADED_PATTERNS)) {
4555
return "overloaded";
4656
}
4757
if (matchesAnyPattern(text, TIMEOUT_PATTERNS)) {

test/detection.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { describe, expect, it } from "bun:test";
22
import { classifyError } from "../src/detection/classifier.js";
33
import { matchesAnyPattern } from "../src/detection/patterns.js";
4+
import { DEFAULT_PATTERNS } from "../src/config/defaults.js";
45

56
describe("classifyError", () => {
67
it("classifies 429 status code as rate_limit", () => {
@@ -45,6 +46,43 @@ describe("classifyError", () => {
4546
expect(classifyError("RATE LIMIT EXCEEDED")).toBe("rate_limit");
4647
expect(classifyError("Rate Limit Exceeded")).toBe("rate_limit");
4748
});
49+
50+
it("classifies ratelimit (one word) as rate_limit", () => {
51+
expect(classifyError("RateLimitError: too fast")).toBe("rate_limit");
52+
});
53+
54+
it("classifies resource exhausted (Google gRPC) as rate_limit", () => {
55+
expect(classifyError("resource exhausted")).toBe("rate_limit");
56+
expect(classifyError("RESOURCE_EXHAUSTED quota exceeded")).toBe("rate_limit");
57+
});
58+
59+
it("classifies 402 status code as quota_exceeded", () => {
60+
expect(classifyError("Payment required", 402)).toBe("quota_exceeded");
61+
});
62+
63+
it("classifies insufficient credit(s) as quota_exceeded", () => {
64+
expect(classifyError("insufficient credit balance")).toBe("quota_exceeded");
65+
expect(classifyError("Insufficient credits on your account")).toBe("quota_exceeded");
66+
});
67+
68+
it("classifies 529 status code as overloaded (not 5xx)", () => {
69+
expect(classifyError("Overloaded", 529)).toBe("overloaded");
70+
expect(classifyError("some error", 529)).toBe("overloaded");
71+
});
72+
});
73+
74+
describe("DEFAULT_PATTERNS gate coverage", () => {
75+
it("passes ratelimit through the gate", () => {
76+
expect(matchesAnyPattern("RateLimitError", DEFAULT_PATTERNS)).toBe(true);
77+
});
78+
79+
it("passes resource exhausted through the gate", () => {
80+
expect(matchesAnyPattern("resource exhausted quota", DEFAULT_PATTERNS)).toBe(true);
81+
});
82+
83+
it("passes insufficient credit through the gate", () => {
84+
expect(matchesAnyPattern("insufficient credit balance", DEFAULT_PATTERNS)).toBe(true);
85+
});
4886
});
4987

5088
describe("matchesAnyPattern", () => {

0 commit comments

Comments
 (0)