can1357/oh-my-pi · error
Image download failed (${response.status}): ${rawText}
Error message
Image download failed (${response.status}): ${rawText} What it means
The tool fetches an image from a remote URL and requires an HTTP 2xx; any non-OK response raises this error embedding the status code and the raw response body, which usually explains why the server refused (404 not found, 403 denied, 429 rate-limited, etc.). It surfaces the server's own error text so the failure cause is visible in the tool result.
Source
Thrown at packages/coding-agent/src/tools/image-gen.ts:397
imageUrl: string,
fetchImpl: FetchImpl,
signal?: AbortSignal,
): Promise<InlineImageData> {
if (imageUrl.startsWith("data:")) {
const normalized = normalizeDataUrl(imageUrl.trim());
if (!normalized.mimeType) {
throw new Error("mime_type is required when providing raw base64 data.");
}
if (!normalized.data) {
throw new Error("Image data is empty.");
}
return { data: normalized.data, mimeType: normalized.mimeType };
}
const response = await fetchImpl(imageUrl, { signal });
if (!response.ok) {
const rawText = await response.text();
throw new Error(`Image download failed (${response.status}): ${rawText}`);
}
const contentType = response.headers.get("content-type")?.split(";")[0];
if (!contentType?.startsWith("image/")) {
throw new Error(`Unsupported image type from URL: ${imageUrl}`);
}
const buffer = await response.bytes();
return { data: buffer.toBase64(), mimeType: contentType };
}
function collectOpenRouterResponseText(message: OpenRouterMessage | undefined): string | undefined {
if (!message) return undefined;
if (typeof message.content === "string") {
const trimmed = message.content.trim();
return trimmed.length > 0 ? trimmed : undefined;
}
if (Array.isArray(message.content)) {
const texts = message.content
.filter(part => part.type === "text")View on GitHub (pinned to 9690622007)
Solutions
- Open the URL (or curl it) and read the embedded status/body message in the error to see the server's reason
- For 403/404 on signed URLs, regenerate a fresh signed URL before invoking the tool
- For 429, wait and retry with backoff, or host the image somewhere less rate-limited
- Verify the URL is reachable from the machine running the tool (proxy/VPN/firewall may block it)
Defensive patterns
Strategy: retry
Validate before calling
// optional pre-flight (only for critical flows)
const head = await fetch(imageUrl, { method: "HEAD" });
if (!head.ok) throw new Error(`Pre-flight failed: ${head.status} for ${imageUrl}`); Try / catch
try {
await genImage({ imageUrl });
} catch (err) {
const m = /Image download failed \((\d+)\)/.exec(String(err));
if (m && (m[1] === "429" || m[1].startsWith("5"))) {
await Bun.sleep(2000); // retry with backoff
return genImage({ imageUrl });
}
throw err; // 4xx: fix URL/auth, don't blind-retry
} Prevention
- Generate fresh signed URLs shortly before use; they expire
- Prefer stable, authenticated hosts over ad-hoc CDNs
- Catch 429/5xx for retries but not 403/404
- Test image URLs with curl -I from the machine running the tool
When it happens
Trigger: loadImageFromUrl calls fetchImpl(imageUrl) and response.ok is false — any 4xx/5xx from the remote image host, e.g. expired signed URL, wrong path, CDN auth, or rate limiting during image-gen input-image collection.
Common situations: Pre-signed S3/GCS URLs that expired; hotlink protection or missing auth headers on a CDN; typo'd image URLs; 429s from provider throttling; image host behind a proxy returning HTML error pages.
Related errors
- V2 remote compaction failed (${response.status} ${response.s
- sso-role
- HTTP request failed. status=${response.status}; url=${url};
- ${response.status} ${response.statusText}: ${text}
- HTTP ${response.status} from ${baseUrl}/api/tags
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/fa2c9438fb0685b7.
Report an issue: GitHub.