diff --git a/packages/ai/src/provider-error.ts b/packages/ai/src/provider-error.ts index dcdf68aeb39..000e0d40d3b 100644 --- a/packages/ai/src/provider-error.ts +++ b/packages/ai/src/provider-error.ts @@ -109,7 +109,8 @@ const QUOTA_CODES = new Set([ "freeusagelimiterror", "creditlimitexceeded", ]) -const AUTH_CODES = new Set(["authentication_error", "permission_error"]) +// Google reports an invalid API key as HTTP 400 INVALID_ARGUMENT with this `details[].reason`. +const AUTH_CODES = new Set(["authentication_error", "permission_error", "api_key_invalid"]) const SERVER_CODES = new Set([ "api_error", "internal_error", @@ -259,6 +260,10 @@ function providerCodes(value: unknown) { error?.type, error?.status, error?.error_type, + // Google `google.rpc.ErrorInfo` details carry the specific reason. + ...(Array.isArray(error?.details) + ? error.details.map((detail) => (isRecord(detail) ? detail.reason : undefined)) + : []), inner?.code, metadata?.error_type, responseError?.code, diff --git a/packages/ai/test/provider-error.test.ts b/packages/ai/test/provider-error.test.ts index 66001973931..457f8a4de8b 100644 --- a/packages/ai/test/provider-error.test.ts +++ b/packages/ai/test/provider-error.test.ts @@ -355,6 +355,32 @@ describe("provider error rawBody classification", () => { } }) + test("classifies Google invalid API keys as authentication failures", () => { + const rawBody = JSON.stringify({ + error: { + code: 400, + message: "API key not valid. Please pass a valid API key.", + status: "INVALID_ARGUMENT", + details: [ + { + "@type": "type.googleapis.com/google.rpc.ErrorInfo", + reason: "API_KEY_INVALID", + domain: "googleapis.com", + }, + { + "@type": "type.googleapis.com/google.rpc.LocalizedMessage", + locale: "en-US", + message: "API key not valid. Please pass a valid API key.", + }, + ], + }, + }) + expect( + classifyProviderFailure({ message: "API key not valid. Please pass a valid API key.", status: 400, rawBody }) + ._tag, + ).toBe("Authentication") + }) + test("classifies overflow signals buried in the raw payload when the summary is vague", () => { const reason = classifyProviderFailure({ message: "Request failed",