From 579828b2d540441928f2c4c96f8e0bbb2ef40115 Mon Sep 17 00:00:00 2001 From: alejandro maza Date: Wed, 7 Jan 2026 07:51:04 -0600 Subject: [PATCH] Handle 413 context overflow errors gracefully When the conversation context exceeds the model's limit, instead of throwing an opaque error or returning raw JSON, we now: 1. Detect context overflow errors (413, request_too_large, etc.) 2. Return a user-friendly message explaining the issue 3. Suggest using /new or /reset to start fresh This prevents the assistant from becoming completely unresponsive when context grows too large (e.g., from many screenshots or long tool outputs). Addresses issue #394 --- src/agents/pi-embedded-helpers.test.ts | 40 +++++++++++++++++++++++++- src/agents/pi-embedded-helpers.ts | 20 +++++++++++++ src/agents/pi-embedded-runner.ts | 21 ++++++++++++++ 3 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/agents/pi-embedded-helpers.test.ts b/src/agents/pi-embedded-helpers.test.ts index c36664dba..726c34565 100644 --- a/src/agents/pi-embedded-helpers.test.ts +++ b/src/agents/pi-embedded-helpers.test.ts @@ -1,6 +1,12 @@ import { describe, expect, it } from "vitest"; -import { buildBootstrapContextFiles } from "./pi-embedded-helpers.js"; +import type { AssistantMessage } from "@mariozechner/pi-ai"; + +import { + buildBootstrapContextFiles, + formatAssistantErrorText, + isContextOverflowError, +} from "./pi-embedded-helpers.js"; import { DEFAULT_AGENTS_FILENAME, type WorkspaceBootstrapFile, @@ -46,3 +52,35 @@ describe("buildBootstrapContextFiles", () => { expect(result?.content.endsWith(long.slice(-120))).toBe(true); }); }); + +describe("isContextOverflowError", () => { + it("matches known overflow hints", () => { + const samples = [ + "request_too_large", + "Request exceeds the maximum size", + "context length exceeded", + "Maximum context length", + "413 Request Entity Too Large", + ]; + for (const sample of samples) { + expect(isContextOverflowError(sample)).toBe(true); + } + }); + + it("ignores unrelated errors", () => { + expect(isContextOverflowError("rate limit exceeded")).toBe(false); + }); +}); + +describe("formatAssistantErrorText", () => { + const makeAssistantError = (errorMessage: string): AssistantMessage => + ({ + stopReason: "error", + errorMessage, + }) as AssistantMessage; + + it("returns a friendly message for context overflow", () => { + const msg = makeAssistantError("request_too_large"); + expect(formatAssistantErrorText(msg)).toContain("Context overflow"); + }); +}); diff --git a/src/agents/pi-embedded-helpers.ts b/src/agents/pi-embedded-helpers.ts index 750c9504b..0b0eaec19 100644 --- a/src/agents/pi-embedded-helpers.ts +++ b/src/agents/pi-embedded-helpers.ts @@ -126,6 +126,18 @@ export function buildBootstrapContextFiles( return result; } +export function isContextOverflowError(errorMessage?: string): boolean { + if (!errorMessage) return false; + const lower = errorMessage.toLowerCase(); + return ( + lower.includes("request_too_large") || + lower.includes("request exceeds the maximum size") || + lower.includes("context length exceeded") || + lower.includes("maximum context length") || + (lower.includes("413") && lower.includes("too large")) + ); +} + export function formatAssistantErrorText( msg: AssistantMessage, ): string | undefined { @@ -133,6 +145,14 @@ export function formatAssistantErrorText( const raw = (msg.errorMessage ?? "").trim(); if (!raw) return "LLM request failed with an unknown error."; + // Check for context overflow (413) errors + if (isContextOverflowError(raw)) { + return ( + "Context overflow: the conversation history is too large. " + + "Use /new or /reset to start a fresh session." + ); + } + const invalidRequest = raw.match( /"type":"invalid_request_error".*?"message":"([^"]+)"/, ); diff --git a/src/agents/pi-embedded-runner.ts b/src/agents/pi-embedded-runner.ts index 86b790a44..e5a75a473 100644 --- a/src/agents/pi-embedded-runner.ts +++ b/src/agents/pi-embedded-runner.ts @@ -59,6 +59,7 @@ import { formatAssistantErrorText, isAuthAssistantError, isAuthErrorMessage, + isContextOverflowError, isRateLimitAssistantError, isRateLimitErrorMessage, pickFallbackThinkingLevel, @@ -1153,6 +1154,26 @@ export async function runEmbeddedPiAgent(params: { } if (promptError && !aborted) { const errorText = describeUnknownError(promptError); + if (isContextOverflowError(errorText)) { + return { + payloads: [ + { + text: + "Context overflow: the conversation history is too large for the model. " + + "Use /new or /reset to start a fresh session, or try a model with a larger context window.", + isError: true, + }, + ], + meta: { + durationMs: Date.now() - started, + agentMeta: { + sessionId: sessionIdUsed, + provider, + model: model.id, + }, + }, + }; + } if ( (isAuthErrorMessage(errorText) || isRateLimitErrorMessage(errorText)) &&