fix(ai): classify xAI invalid API keys as authentication errors

This commit is contained in:
Aiden Cline
2026-09-29 11:43:59 -05:00
parent 599bdc982e
commit 316f03156c
2 changed files with 17 additions and 2 deletions
+8 -1
View File
@@ -147,6 +147,8 @@ const CONTENT_POLICY_CODES = new Set([
// OpenCode Zen replaces upstream codes outside its allow-list but keeps the original // OpenCode Zen replaces upstream codes outside its allow-list but keeps the original
// as a `[code]` label at the start of the rewritten message. // as a `[code]` label at the start of the rewritten message.
const GATEWAY_CODE_LABEL = /^[^:\n]+: \[([A-Za-z0-9_.-]+)\]/ const GATEWAY_CODE_LABEL = /^[^:\n]+: \[([A-Za-z0-9_.-]+)\]/
// xAI reports an invalid API key as HTTP 400 with the generic `invalid-argument` code.
const AUTH_TEXT = /incorrect api key provided/i
const RATE_LIMIT_TEXT = /rate increased too quickly|rate[-_\s]?limit|too[_\s]?many[_\s]?requests/i const RATE_LIMIT_TEXT = /rate increased too quickly|rate[-_\s]?limit|too[_\s]?many[_\s]?requests/i
// Only consulted on 429, where throttles and account caps share a status. // Only consulted on 429, where throttles and account caps share a status.
const QUOTA_TEXT = /insufficient[-_\s]?quota|quota[-_\s]?exceeded|budget exceeded|usage limit/i const QUOTA_TEXT = /insufficient[-_\s]?quota|quota[-_\s]?exceeded|budget exceeded|usage limit/i
@@ -246,7 +248,12 @@ export function classifyProviderFailure(input: ProviderFailure): AIError["reason
(input.status === 429 && QUOTA_TEXT.test(text)) (input.status === 429 && QUOTA_TEXT.test(text))
) )
return new QuotaExceededError(details) return new QuotaExceededError(details)
if (input.status === 401 || input.status === 403 || codes.some((code) => AUTH_CODES.has(code))) if (
input.status === 401 ||
input.status === 403 ||
codes.some((code) => AUTH_CODES.has(code)) ||
(input.status === 400 && AUTH_TEXT.test(text))
)
return new AuthenticationError(details) return new AuthenticationError(details)
if ( if (
input.status === 429 || input.status === 429 ||
+9 -1
View File
@@ -383,7 +383,7 @@ describe("provider error rawBody classification", () => {
expect(reason._tag === "InvalidRequest" ? reason.classification : reason._tag).toBe("context-overflow") expect(reason._tag === "InvalidRequest" ? reason.classification : reason._tag).toBe("context-overflow")
}) })
test("classifies Google invalid API keys as authentication failures", () => { test("classifies invalid API keys reported as HTTP 400 as authentication failures", () => {
const rawBody = JSON.stringify({ const rawBody = JSON.stringify({
error: { error: {
code: 400, code: 400,
@@ -407,6 +407,14 @@ describe("provider error rawBody classification", () => {
classifyProviderFailure({ message: "API key not valid. Please pass a valid API key.", status: 400, rawBody }) classifyProviderFailure({ message: "API key not valid. Please pass a valid API key.", status: 400, rawBody })
._tag, ._tag,
).toBe("Authentication") ).toBe("Authentication")
// xAI
expect(
classifyProviderFailure({
message: "Incorrect API key provided. You can obtain an API key from https://console.x.ai.",
status: 400,
rawBody: '{"code":"invalid-argument","error":"Incorrect API key provided."}',
})._tag,
).toBe("Authentication")
}) })
test("classifies overflow signals buried in the raw payload when the summary is vague", () => { test("classifies overflow signals buried in the raw payload when the summary is vague", () => {