Skip to content
This repository was archived by the owner on Aug 6, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion packages/agent/src/adapters/error-classification.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { describe, expect, it } from "vitest";
import { classifyAgentError } from "./error-classification";
import {
classifyAgentError,
isPromptTooLongError,
} from "./error-classification";

describe("classifyAgentError", () => {
it.each([
Expand All @@ -22,6 +25,8 @@ describe("classifyAgentError", () => {
["API Error: 429 rate limited", "upstream_provider_failure"],
["API Error: 529 overloaded", "upstream_provider_failure"],
["API Error: 400 invalid request", "agent_error"],
// 413 is a hard client rejection, never a transient upstream failure.
["API Error: 413 Payload Too Large", "agent_error"],
[
"Connection closed mid-response without the API Error prefix",
"agent_error",
Expand All @@ -32,3 +37,40 @@ describe("classifyAgentError", () => {
expect(classifyAgentError(message)).toBe(expected);
});
});

describe("isPromptTooLongError", () => {
it.each([
[
'API Error: 400 {"type":"error","error":{"type":"invalid_request_error","message":"prompt is too long: 214431 tokens > 204698 maximum"}}',
true,
],
[
'API Error: 413 {"error":{"message":"litellm.ContextWindowExceededError: The estimated number of input and maximum output tokens (262334) exceeded this model context window limit (262144)","code":"5021"}}',
true,
],
// Must match without the "API Error: 413" prefix.
[
"litellm.ContextWindowExceededError: The estimated number of input and maximum output tokens (262334) exceeded this model context window limit (262144)",
true,
],
// The ACP-wrapped shape the agent-server catch actually sees.
[
'Internal error: API Error: 413 {"error":{"message":"exceeded this model context window limit (262144)"}}',
true,
],
// Any gateway 413 means an oversized payload, whatever the body text.
["API Error: 413 Payload Too Large", true],
// Pins the 413 matcher's i flag.
["api error: 413 payload too large", true],
["API Error: 429 rate limited", false],
["API Error: 400 invalid request", false],
["some unrelated failure", false],
] as const)("detects %j as %s", (message, expected) => {
expect(isPromptTooLongError(new Error(message))).toBe(expected);
});

it("handles non-Error inputs", () => {
expect(isPromptTooLongError({ message: "prompt is too long" })).toBe(true);
expect(isPromptTooLongError(undefined)).toBe(false);
});
});
13 changes: 11 additions & 2 deletions packages/agent/src/adapters/error-classification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,16 @@ export function classifyAgentError(
return "agent_error";
}

/** Hard API rejection: the assembled prompt exceeds the model's context window. */
/**
* Hard API rejection: the prompt exceeds the model's context window
* (Anthropic phrasing, or the LLM gateway's HTTP 413). Retrying the same
* transcript can never succeed; callers must shrink the prompt.
*/
export function isPromptTooLongError(error: unknown): boolean {
return /prompt is too long/i.test(getErrorMessage(error));
const message = getErrorMessage(error);
return (
/prompt is too long/i.test(message) ||
/exceeded this model context window limit/i.test(message) ||
/API Error:\s*413\b/i.test(message)
);
}
24 changes: 20 additions & 4 deletions packages/agent/src/server/agent-server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3406,19 +3406,35 @@ describe("AgentServer HTTP Mode", () => {
});

it.each([
{ retryOutcome: "succeeds", retryFails: false },
{ retryOutcome: "fails", retryFails: true },
{
retryOutcome: "succeeds",
retryFails: false,
oversizedError: "Internal error: Prompt is too long",
},
{
retryOutcome: "fails",
retryFails: true,
oversizedError: "Internal error: Prompt is too long",
},
// The LLM gateway phrases oversized rejections as HTTP 413; the
// fresh-session retry must trigger on that shape too.
{
retryOutcome: "succeeds after a gateway 413",
retryFails: false,
oversizedError:
'Internal error: API Error: 413 {"error":{"message":"litellm.ContextWindowExceededError: The estimated number of input and maximum output tokens (262334) exceeded this model context window limit (262144)","code":"5021"}}',
},
])(
"clears resume state when the fresh-session retry $retryOutcome",
async ({ retryFails }) => {
async ({ retryFails, oversizedError }) => {
const s = createServer();
await s.start();

const prompts: ContentBlock[][] = [];
const prompt = vi.fn(async (params: { prompt: ContentBlock[] }) => {
prompts.push(params.prompt);
if (prompts.length === 1) {
throw new Error("Internal error: Prompt is too long");
throw new Error(oversizedError);
}
if (retryFails) {
throw new Error("Fresh-session retry failed");
Expand Down
Loading