From e3cd5c97d02b9605d2609d5cd76e444d7475c874 Mon Sep 17 00:00:00 2001 From: Cody Moore Date: Sun, 2 Aug 2026 12:42:55 -0400 Subject: [PATCH] fix(llm): classify Azure payload-size and image-count 400s as context overflow Azure OpenAI enforces two per-request caps that are returned as 400s with prose messages rather than 413s: Request content length exceeded 32 MB limit. Exceeded maximum number of images (50) allowed in the request. Neither matched isContextOverflow, so media-strip compaction never ran and the oversized history was resent on every turn, leaving the session permanently unrecoverable (/compact fails identically). The existing 413 handling and /request entity too large/ pattern already cover this semantic failure for the 413 spelling; these two patterns extend the same treatment to Azure's 400 phrasing. Note the near-miss against /context[_ ]length[_ ]exceeded/ - Azure says "content length", not "context length". Patterns are placed after the exclusions list so throttling and rate-limit messages are still correctly excluded. --- packages/llm/src/provider-error.ts | 2 ++ packages/llm/test/provider-error.test.ts | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/llm/src/provider-error.ts b/packages/llm/src/provider-error.ts index f8b8a5c013e1..983baea753e5 100644 --- a/packages/llm/src/provider-error.ts +++ b/packages/llm/src/provider-error.ts @@ -21,6 +21,8 @@ const patterns = [ /exceeded model token limit/i, /context[_ ]length[_ ]exceeded/i, /request entity too large/i, + /content length exceeded/i, + /maximum number of images/i, /context length is only \d+ tokens/i, /input length.*exceeds.*context length/i, /prompt too long; exceeded (?:max )?context length/i, diff --git a/packages/llm/test/provider-error.test.ts b/packages/llm/test/provider-error.test.ts index c4185969640a..f36f6099ea25 100644 --- a/packages/llm/test/provider-error.test.ts +++ b/packages/llm/test/provider-error.test.ts @@ -18,6 +18,21 @@ describe("provider error classification", () => { expect(messages.every(isContextOverflow)).toBe(true) }) + test("classifies provider request payload and media caps as context overflow", () => { + const messages = [ + // Azure OpenAI request payload byte cap, returned as a 400 (not a 413) + "Request content length exceeded 32 MB limit.", + '400 status code: {"error":{"message":"Request content length exceeded 32 MB limit.","type":"invalid_request_error","param":null,"code":null}}', + // Azure OpenAI per-request image cap + "Exceeded maximum number of images (50) allowed in the request.", + // OpenRouter double-wraps the upstream provider body as an escaped string in metadata.raw + '{"error":{"message":"Provider returned error","code":400,"metadata":{"raw":"{\\"error\\": {\\"message\\": \\"Request content length exceeded 32 MB limit.\\"}}","provider_name":"Azure"}}}', + '{"error":{"message":"Provider returned error","code":400,"metadata":{"raw":"{\\"error\\": {\\"message\\": \\"Exceeded maximum number of images (50) allowed in the request.\\"}}","provider_name":"Azure"}}}', + ] + + expect(messages.every(isContextOverflow)).toBe(true) + }) + test("does not classify rate limits as context overflow", () => { const messages = [ "Throttling error: Too many tokens, please wait before trying again.",