diff --git a/packages/ai/src/provider-error.ts b/packages/ai/src/provider-error.ts index b1f3c0a70f2..e1f24c23f19 100644 --- a/packages/ai/src/provider-error.ts +++ b/packages/ai/src/provider-error.ts @@ -262,7 +262,7 @@ export function classifyProviderFailure(input: ProviderFailure): AIError["reason (codes.includes("context_length_exceeded") || codes.includes("model_context_window_exceeded") || codes.includes("request_too_large") || - isContextOverflow(text)) + (!codes.includes("throttlingexception") && isContextOverflow(text))) ) return new InvalidRequestError({ ...details, classification: "context-overflow" }) if (input.status === 413 || isPayloadTooLarge(text)) diff --git a/packages/ai/test/provider-error.test.ts b/packages/ai/test/provider-error.test.ts index 06e7703a287..069181e722d 100644 --- a/packages/ai/test/provider-error.test.ts +++ b/packages/ai/test/provider-error.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test" import { isContextOverflow, isContextOverflowFailure, isRetryable } from "../src/effect.js" -import { AIError } from "../src/schema/index.js" +import { AIError, HttpContext } from "../src/schema/index.js" import { classifyProviderFailure, providerErrorMessage } from "../src/provider-error.js" describe("provider error classification", () => { @@ -449,6 +449,37 @@ describe("provider error classification", () => { expect(isContextOverflow("Too many tokens")).toBe(true) }) + test("does not infer context overflow from known throttling exceptions", () => { + const cases = [ + { status: 429, data: { code: "ThrottlingException" } }, + { status: 429, rawBody: '{"__type":"com.amazonaws.bedrock#ThrottlingException"}' }, + { rawBody: '{"exception":{"type":"throttlingException"}}' }, + { + status: 429, + http: new HttpContext({ + url: "https://bedrock.test/model/example/converse-stream", + status: 429, + headers: { "x-amzn-errortype": "ThrottlingException:http://internal.amazon.com/coral/" }, + }), + }, + ] + for (const input of cases) { + for (const message of ["Token limit exceeded", "Too many tokens, please try again later"]) { + const error = new AIError({ reason: classifyProviderFailure({ ...input, message }) }) + expect(error.reason._tag).toBe("RateLimit") + expect(isContextOverflowFailure(error)).toBe(false) + expect(isRetryable(error)).toBe(true) + } + } + expect( + classifyProviderFailure({ + message: "prompt is too long", + status: 400, + data: { code: "ValidationException" }, + }), + ).toMatchObject({ _tag: "InvalidRequest", classification: "context-overflow" }) + }) + test("does not let substituted server codes make a 4xx retryable", () => { const openai = { error: { type: "server_error", message: "Upstream request failed: Model is unavailable." } } const anthropic = {