{"record":{"id":"33dbb707b2438a30","repo":"decolua/9router","slug":"failed-to-fetch-image-res-status","errorCode":null,"errorMessage":"Failed to fetch image: ${res.status}","messagePattern":"Failed to fetch image: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/handlers/imageProviders/_base.js","lineNumber":24,"sourceCode":"export const sleep = (ms) => new Promise((r) => setTimeout(r, ms));\n\n// Map OpenAI size to provider-specific aspect ratio\nexport function sizeToAspectRatio(size) {\n  if (!size || typeof size !== \"string\") return \"1:1\";\n  const map = {\n    \"1024x1024\": \"1:1\",\n    \"1024x1792\": \"9:16\",\n    \"1792x1024\": \"16:9\",\n    \"1024x1536\": \"2:3\",\n    \"1536x1024\": \"3:2\",\n  };\n  return map[size] || \"1:1\";\n}\n\n// Fetch URL → base64 (for providers returning image URLs)\nexport async function urlToBase64(url) {\n  const res = await fetch(url);\n  if (!res.ok) throw new Error(`Failed to fetch image: ${res.status}`);\n  const buf = await res.arrayBuffer();\n  return Buffer.from(buf).toString(\"base64\");\n}\n\nexport function nowSec() {\n  return Math.floor(Date.now() / 1000);\n}\n","sourceCodeStart":6,"sourceCodeEnd":32,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/imageProviders/_base.js#L6-L32","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst res = await fetch(url);\n// after: retry with backoff before giving up\nlet res;\nfor (let i = 0; i < 3; i++) {\n  res = await fetch(url);\n  if (res.ok) break;\n  await new Promise(r => setTimeout(r, 1000 * (i + 1)));\n}\nif (!res.ok) throw new Error(`Failed to fetch image: ${res.status}`);","handlingStrategy":"retry","validationCode":"async function isUrlFetchable(url) {\n  try { const r = await fetch(url, { method: \"HEAD\" }); return r.ok; } catch { return false; }\n}","typeGuard":"function isHttpUrl(v) {\n  try { const u = new URL(v); return u.protocol === \"https:\" || u.protocol === \"http:\"; } catch { return false; }\n}","tryCatchPattern":"try {\n  const b64 = await urlToBase64(imageUrl);\n} catch (e) {\n  if (/Failed to fetch image: (429|5\\d\\d)/.test(e.message)) {\n    await delay(2000);\n    return retryUrlToBase64(imageUrl, 3);\n  }\n  if (/Failed to fetch image: (403|404)/.test(e.message)) {\n    return regenerateImage(prompt); // signed URL expired or purged\n  }\n  throw e;\n}","preventionTips":["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."],"tags":["network","http","image-generation","fetch","url-fetch"],"backgroundTag":"image-fetch-failed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}