Bug: Oversized image in tool result causes infinite error loop
Summary
When the read tool returns an image that exceeds Anthropic's 5MB base64 limit, the API returns a 400 invalid_request_error. The oversized image stays in message history, so every subsequent API call fails with the same error — creating an infinite loop that locks up the chat.
Error
Error: 400
{"type":"error","error":{"type":"invalid_request_error","message":"messages.31.content.0.tool_result.content.1.image.source.base64: image exceeds 5 MB maximum: 5529508 bytes > 5242880 bytes"},"request_id":"req_011CYwW5ktEeTQ8L5jKqt4G4"}
Root Cause
Three gaps in the image size handling:
1. image-resize.js — Fallback paths return oversized originals
When Photon fails to load (loadPhoton() returns null) or image processing throws in the catch block, resizeImage() returns the original unchanged image — which may be >5MB:
// Line ~33: Photon not available
if (!photon) {
return { data: img.data, ... wasResized: false }; // ← original, could be >5MB
}
// Line ~152: catch block
catch {
return { data: img.data, ... wasResized: false }; // ← original, could be >5MB
}
The "last resort" return (line ~141) also doesn't guarantee the result is under the limit.
2. read.js — No post-resize size validation
The read tool calls resizeImage() and blindly sends whatever comes back — no final check that the result is actually under 5MB:
const resized = await resizeImage({ type: "image", data: base64, mimeType });
// No size check here
content = [
{ type: "text", text: textNote },
{ type: "image", data: resized.data, mimeType: resized.mimeType }, // ← could be >5MB
];
3. agent-session.js — No recovery from image-too-large 400 errors
_isRetryableError() only matches 429/500/502/503/504/overloaded/rate-limit errors. The 400 invalid_request_error for oversized images is NOT matched, so:
- The error gets stored as an assistant message with
stopReason: "error"
- The oversized image tool result stays in message history
- Every subsequent
agent.continue() or new user message sends the same oversized image → same 400 → infinite loop
Proposed Fix
Layer 1: image-resize.js
- When Photon is unavailable or processing fails AND the original exceeds
maxBytes, return null instead of the original
- Add a comment that callers must handle
null return
Layer 2: read.js
- Handle
null return from resizeImage() — return a text-only error explaining the image is too large
- Add a final safety check: if the resized result is still >5MB, return text error instead
Layer 3: agent-session.js
- In the
agent_end handler, detect image exceeds 5 MB errors before checking retryable errors
- Strip oversized image blocks from message history (replace with text explanations)
- Remove the error assistant message so the agent can continue cleanly
Environment
- pi-coding-agent (npm, macOS ARM64)
- Anthropic Claude API (5MB per-image base64 limit)
- Photon loads successfully — issue occurs in edge cases where resize still exceeds limit, or in the fallback/catch paths
Bug: Oversized image in tool result causes infinite error loop
Summary
When the
readtool returns an image that exceeds Anthropic's 5MB base64 limit, the API returns a 400invalid_request_error. The oversized image stays in message history, so every subsequent API call fails with the same error — creating an infinite loop that locks up the chat.Error
Root Cause
Three gaps in the image size handling:
1.
image-resize.js— Fallback paths return oversized originalsWhen Photon fails to load (
loadPhoton()returns null) or image processing throws in the catch block,resizeImage()returns the original unchanged image — which may be >5MB:The "last resort" return (line ~141) also doesn't guarantee the result is under the limit.
2.
read.js— No post-resize size validationThe read tool calls
resizeImage()and blindly sends whatever comes back — no final check that the result is actually under 5MB:3.
agent-session.js— No recovery from image-too-large 400 errors_isRetryableError()only matches 429/500/502/503/504/overloaded/rate-limit errors. The 400invalid_request_errorfor oversized images is NOT matched, so:stopReason: "error"agent.continue()or new user message sends the same oversized image → same 400 → infinite loopProposed Fix
Layer 1:
image-resize.jsmaxBytes, returnnullinstead of the originalnullreturnLayer 2:
read.jsnullreturn fromresizeImage()— return a text-only error explaining the image is too largeLayer 3:
agent-session.jsagent_endhandler, detectimage exceeds 5 MBerrors before checking retryable errorsEnvironment