decolua/9router · error
Failed to fetch image: ${res.status}
Error message
Failed to fetch image: ${res.status} What it means
Thrown by urlToBase64 (open-sse/handlers/imageProviders/_base.js:24) when fetching an image URL returned by a generation provider fails with a non-OK HTTP status. Some image providers (e.g. those returning hosted URLs instead of base64) require a second fetch to download the image so it can be returned as b64_json; this error surfaces the upstream status of that download, not of the generation call itself.
Source
Thrown at open-sse/handlers/imageProviders/_base.js:24
export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
// Map OpenAI size to provider-specific aspect ratio
export function sizeToAspectRatio(size) {
if (!size || typeof size !== "string") return "1:1";
const map = {
"1024x1024": "1:1",
"1024x1792": "9:16",
"1792x1024": "16:9",
"1024x1536": "2:3",
"1536x1024": "3:2",
};
return map[size] || "1:1";
}
// Fetch URL → base64 (for providers returning image URLs)
export async function urlToBase64(url) {
const res = await fetch(url);
if (!res.ok) throw new Error(`Failed to fetch image: ${res.status}`);
const buf = await res.arrayBuffer();
return Buffer.from(buf).toString("base64");
}
export function nowSec() {
return Math.floor(Date.now() / 1000);
}
View on GitHub (pinned to 90b52e06ff)
Solutions
- Retry the generation (or just the URL fetch after a short delay) — transient 5xx/429 on the CDN usually clears.
- Fetch and convert the URL to base64 immediately after generation before any signed URL expires.
- Check the status code in the message: 403 usually means the URL requires auth/headers — consider proxying the fetch with appropriate headers; 404 means the asset is gone and the image must be regenerated.
- If a provider consistently returns undownloadable URLs, prefer a provider/response mode that returns b64 directly.
Example fix
// before
const res = await fetch(url);
// after: retry with backoff before giving up
let res;
for (let i = 0; i < 3; i++) {
res = await fetch(url);
if (res.ok) break;
await new Promise(r => setTimeout(r, 1000 * (i + 1)));
}
if (!res.ok) throw new Error(`Failed to fetch image: ${res.status}`); Defensive patterns
Strategy: retry
Validate before calling
async function isUrlFetchable(url) {
try { const r = await fetch(url, { method: "HEAD" }); return r.ok; } catch { return false; }
} Type guard
function isHttpUrl(v) {
try { const u = new URL(v); return u.protocol === "https:" || u.protocol === "http:"; } catch { return false; }
} Try / catch
try {
const b64 = await urlToBase64(imageUrl);
} catch (e) {
if (/Failed to fetch image: (429|5\d\d)/.test(e.message)) {
await delay(2000);
return retryUrlToBase64(imageUrl, 3);
}
if (/Failed to fetch image: (403|404)/.test(e.message)) {
return regenerateImage(prompt); // signed URL expired or purged
}
throw e;
} Prevention
- Download provider image URLs immediately — many are short-lived signed links.
- Retry transient statuses (429, 5xx) with backoff; do not retry 403/404.
- Send browser-like headers (User-Agent/Referer) for hosts with hotlink protection.
- Prefer providers/endpoints that return b64_json directly when available.
When it happens
Trigger: A provider's image result contains an image URL; urlToBase64 is called (from handleImageGenerationCore or b64 helpers) and fetch(url) resolves with res.ok === false — 403 (URL signed/expired or hotlink-protected), 404 (host purged the file), 429 (rate limited), or 5xx from the image CDN.
Common situations: Provider URLs expire within minutes (common with CDN-signed links) and the app fetched too late; the image host blocks requests without a Referer/User-Agent; transient CDN errors during a burst of generations.
Related errors
- HTTP ${result.response.status}
- Google translate fetch failed: ${res.status}
- Google TTS failed: ${res.status}
- MiniMax TTS error (${res.status})
- loadCodeAssist failed: HTTP ${response.status} ${errorText.s
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/33dbb707b2438a30.
Report an issue: GitHub.