fix(usage): make opencode-go quota fetcher fail-open instead of throwing 500 (#3522)

Integrated into release/v3.8.20
This commit is contained in:
Wilson
2026-06-10 09:00:01 -03:00
committed by GitHub
parent 4061207a49
commit 1df64d11eb
2 changed files with 58 additions and 16 deletions

View File

@@ -928,17 +928,25 @@ async function getOpenCodeGoUsage(apiKey: string) {
});
if (!res.ok) {
if (res.status === 401 || res.status === 403) throw new Error("Invalid OpenCode Go API key");
throw new Error(`OpenCode Go quota API error (${res.status})`);
if (res.status === 401 || res.status === 403) {
return { message: "OpenCode Go quota endpoint rejected this API key. Chat requests still work." };
}
return { message: `OpenCode Go quota API error (${res.status})` };
}
const json = await res.json();
const code = toNumber(json.code, 200);
if (code === 401 || code === 403 || json.success === false) {
throw new Error("Invalid OpenCode Go API key");
let json: unknown;
try {
json = await res.json();
} catch {
return { message: "OpenCode Go quota response parsing failed." };
}
const data = toRecord(json.data);
const code = toNumber((json as Record<string, unknown>).code, 200);
if (code === 401 || code === 403 || (json as Record<string, unknown>).success === false) {
return { message: "OpenCode Go quota endpoint rejected this API key. Chat requests still work." };
}
const data = toRecord((json as Record<string, unknown>).data);
const limits: unknown[] = Array.isArray(data.limits) ? data.limits : [];
const quotas: Record<string, UsageQuota> = {};

View File

@@ -136,20 +136,54 @@ test("getUsageForProvider exposes OpenCode Go 5h, weekly, and monthly quotas", a
}
});
test("getUsageForProvider rejects invalid OpenCode Go API keys", async () => {
test("getUsageForProvider returns message for invalid OpenCode Go API keys", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () => new Response("nope", { status: 401 });
try {
await assert.rejects(
() =>
usage.getUsageForProvider({
id: "opencode-go-401",
provider: "opencode-go",
apiKey: "bad-key",
}),
/Invalid OpenCode Go API key/
const result = (await usage.getUsageForProvider({
id: "opencode-go-401",
provider: "opencode-go",
apiKey: "bad-key",
})) as { message: string };
assert.equal(result.message, "OpenCode Go quota endpoint rejected this API key. Chat requests still work.");
} finally {
globalThis.fetch = originalFetch;
}
});
test("getUsageForProvider returns message when OpenCode Go quota API returns 200 with auth error in body", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () =>
new Response(
JSON.stringify({ code: 401, msg: "token expired or incorrect", success: false }),
{ status: 200, headers: { "content-type": "application/json" } }
);
try {
const result = (await usage.getUsageForProvider({
id: "opencode-go-body-401",
provider: "opencode-go",
apiKey: "sk-test-key",
})) as { message: string };
assert.equal(result.message, "OpenCode Go quota endpoint rejected this API key. Chat requests still work.");
} finally {
globalThis.fetch = originalFetch;
}
});
test("getUsageForProvider returns message when OpenCode Go quota response is invalid JSON", async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = async () =>
new Response("<html>not json</html>", { status: 200, headers: { "content-type": "text/html" } });
try {
const result = (await usage.getUsageForProvider({
id: "opencode-go-bad-json",
provider: "opencode-go",
apiKey: "sk-test-key",
})) as { message: string };
assert.equal(result.message, "OpenCode Go quota response parsing failed.");
} finally {
globalThis.fetch = originalFetch;
}