From 579828b2d540441928f2c4c96f8e0bbb2ef40115 Mon Sep 17 00:00:00 2001 From: alejandro maza Date: Wed, 7 Jan 2026 07:51:04 -0600 Subject: [PATCH 1/3] 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)) && From 43c7f5036a94572567ab6159ded32dd87ea88764 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 7 Jan 2026 19:04:04 +0000 Subject: [PATCH 2/3] fix(tools): keep tool errors concise --- CHANGELOG.md | 1 + src/agents/pi-embedded-helpers.test.ts | 3 +-- src/agents/pi-tool-definition-adapter.test.ts | 3 ++- src/agents/pi-tool-definition-adapter.ts | 23 +++++++++++++++---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d76591cb..3bb6b2e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ - ClawdbotKit: fix SwiftPM resource bundling path for `tool-display.json`. Thanks @fcatuhe for PR #398. - Tools: add Telegram/WhatsApp reaction tools (with per-provider gating). Thanks @zats for PR #353. - Tools: flatten literal-union schemas for Claude on Vertex AI. Thanks @carlulsoe for PR #409. +- Tools: keep tool failure logs concise (no stack traces); full stack only in debug logs. - Tools: unify reaction removal semantics across Discord/Slack/Telegram/WhatsApp and allow WhatsApp reaction routing across accounts. - Android: fix APK output filename renaming after AGP updates. Thanks @Syhids for PR #410. - Android: rotate camera photos by EXIF orientation. Thanks @fcatuhe for PR #403. diff --git a/src/agents/pi-embedded-helpers.test.ts b/src/agents/pi-embedded-helpers.test.ts index 726c34565..69a93430a 100644 --- a/src/agents/pi-embedded-helpers.test.ts +++ b/src/agents/pi-embedded-helpers.test.ts @@ -1,6 +1,5 @@ -import { describe, expect, it } from "vitest"; - import type { AssistantMessage } from "@mariozechner/pi-ai"; +import { describe, expect, it } from "vitest"; import { buildBootstrapContextFiles, diff --git a/src/agents/pi-tool-definition-adapter.test.ts b/src/agents/pi-tool-definition-adapter.test.ts index 27a101002..48700ad28 100644 --- a/src/agents/pi-tool-definition-adapter.test.ts +++ b/src/agents/pi-tool-definition-adapter.test.ts @@ -22,6 +22,7 @@ describe("pi tool definition adapter", () => { status: "error", tool: "boom", }); - expect(JSON.stringify(result.details)).toContain("nope"); + expect(result.details).toMatchObject({ error: "nope" }); + expect(JSON.stringify(result.details)).not.toContain("\n at "); }); }); diff --git a/src/agents/pi-tool-definition-adapter.ts b/src/agents/pi-tool-definition-adapter.ts index df8b64d8d..9f4451625 100644 --- a/src/agents/pi-tool-definition-adapter.ts +++ b/src/agents/pi-tool-definition-adapter.ts @@ -4,12 +4,23 @@ import type { AgentToolUpdateCallback, } from "@mariozechner/pi-agent-core"; import type { ToolDefinition } from "@mariozechner/pi-coding-agent"; -import { logError } from "../logger.js"; +import { logDebug, logError } from "../logger.js"; import { jsonResult } from "./tools/common.js"; // biome-ignore lint/suspicious/noExplicitAny: TypeBox schema type from pi-agent-core uses a different module instance. type AnyAgentTool = AgentTool; +function describeToolExecutionError(err: unknown): { + message: string; + stack?: string; +} { + if (err instanceof Error) { + const message = err.message?.trim() ? err.message : String(err); + return { message, stack: err.stack }; + } + return { message: String(err) }; +} + export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] { return tools.map((tool) => { const name = tool.name || "tool"; @@ -37,13 +48,15 @@ export function toToolDefinitions(tools: AnyAgentTool[]): ToolDefinition[] { ? String((err as { name?: unknown }).name) : ""; if (name === "AbortError") throw err; - const message = - err instanceof Error ? (err.stack ?? err.message) : String(err); - logError(`[tools] ${tool.name} failed: ${message}`); + const described = describeToolExecutionError(err); + if (described.stack && described.stack !== described.message) { + logDebug(`tools: ${tool.name} failed stack:\n${described.stack}`); + } + logError(`[tools] ${tool.name} failed: ${described.message}`); return jsonResult({ status: "error", tool: tool.name, - error: message, + error: described.message, }); } }, From 3842a6ae6e56a08f3bcf7d0ea5d8948aa7dfc523 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Wed, 7 Jan 2026 19:06:46 +0000 Subject: [PATCH 3/3] docs: credit PR #395 contributor --- CHANGELOG.md | 1 + README.md | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bb6b2e1a..e2730752a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ - Agent: protect bootstrap prefix from context pruning. Thanks @maxsumrall for PR #381. - Agent: deliver final replies for non-streaming models when block chunking is enabled. Thank you @mneves75 for PR #369! - Agent: trim bootstrap context injections and keep group guidance concise (emoji reactions allowed). Thanks @tobiasbischoff for PR #370. +- Agent: return a friendly context overflow response (413/request_too_large). Thanks @alejandroOPI for PR #395. - Sub-agents: allow `sessions_spawn` model overrides and error on invalid models. Thanks @azade-c for PR #298. - Sub-agents: skip invalid model overrides with a warning and keep the run alive; tool exceptions now return tool errors instead of crashing the agent. - Sessions: forward explicit sessionKey through gateway/chat/node bridge to avoid sub-agent sessionId mixups. diff --git a/README.md b/README.md index a6623a632..eb491440b 100644 --- a/README.md +++ b/README.md @@ -454,5 +454,5 @@ Thanks to all clawtributors: adamgall jalehman jarvis-medmatic mneves75 regenrek tobiasbischoff MSch obviyus dbhurley Asleep123 Iamadig imfing kitze nachoiacovino VACInc cash-echo-bot claude kiranjd pcty-nextgen-service-account minghinmatthewlam ngutman onutc oswalpalash snopoke ManuelHettich loukotal hugobarauna AbhisekBasu1 emanuelst dantelex erikpr1994 antons RandyVentures - reeltimeapps fcatuhe maxsumrall carlulsoe + reeltimeapps fcatuhe maxsumrall carlulsoe alejandroOPI