{"record":{"id":"20e2df6fe0cb8700","repo":"linshenkx/prompt-optimizer","slug":"image-request-failed-resp-status","errorCode":null,"errorMessage":"Image request failed: ${resp.status}","messagePattern":"Image request failed: (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/src/utils/image-asset-storage.ts","lineNumber":84,"sourceCode":"  if (\n    bytes.length >= 6 &&\n    bytes[0] === 0x47 &&\n    bytes[1] === 0x49 &&\n    bytes[2] === 0x46 &&\n    bytes[3] === 0x38 &&\n    (bytes[4] === 0x37 || bytes[4] === 0x39) &&\n    bytes[5] === 0x61\n  ) {\n    return 'image/gif'\n  }\n\n  return null\n}\n\nconst fetchImagePayloadFromUrl = async (absoluteUrl: string): Promise<ImagePayload> => {\n  const resp = await fetch(absoluteUrl, { method: 'GET' })\n  if (!resp.ok) {\n    throw new Error(`Image request failed: ${resp.status}`)\n  }\n\n  const headerType = resp.headers.get('content-type')\n  const mimeType = typeof headerType === 'string' ? headerType.split(';')[0].trim() : ''\n  const ab = await resp.arrayBuffer()\n  const bytes = new Uint8Array(ab)\n  const inferredMimeType = inferMimeTypeFromBytes(bytes)\n  const finalMimeType =\n    mimeType && mimeType !== 'application/octet-stream'\n      ? mimeType\n      : inferredMimeType || mimeType || 'application/octet-stream'\n\n  type BufferLike = {\n    from: (data: ArrayBuffer) => { toString: (encoding: 'base64') => string }\n  }\n\n  const maybeBuffer = (globalThis as unknown as { Buffer?: BufferLike }).Buffer\n  if (maybeBuffer && typeof maybeBuffer.from === 'function') {","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/ui/src/utils/image-asset-storage.ts#L66-L102","documentation":"fetchImagePayloadFromUrl performs a GET on an absolute URL and throws when the response status is not ok (outside 2xx). The message includes the HTTP status code, e.g. 'Image request failed: 404'. It is a hard failure from normalizeImageSourceToPayload's remote-fetch path.","triggerScenarios":"Passing a URL that returns 404 (deleted asset), 403/401 (expired signed URL or private bucket), 410, or a 5xx; CORS preflight failures surface as fetch rejections while non-ok statuses surface here.","commonSituations":"Expired S3/CDN signed URLs; hotlink-protected image hosts returning 403; deleted attachments; server briefly returning 502/503; typo'd URL paths.","solutions":["Check the status in the message: 404 means the URL is wrong/deleted, 403 means auth/hotlink protection, 5xx means retry later","If using signed URLs, generate a fresh signed URL before fetching","For transient 5xx, retry with backoff or fall back to a placeholder","Verify the URL opens in a browser/incognito and that CORS headers allow your origin"],"exampleFix":"// before\nconst payload = await normalizeImageSourceToPayload(url)\n\n// after\nlet payload\ntry {\n  payload = await normalizeImageSourceToPayload(url)\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Image request failed:')) {\n    payload = PLACEHOLDER_PAYLOAD // or re-sign URL and retry\n  } else throw e\n}","handlingStrategy":"retry","validationCode":"const probe = await fetch(url, { method: 'HEAD' })\nif (!probe.ok) {\n  if (probe.status === 403 || probe.status === 401) await refreshSignedUrl()\n  else throw new Error(`Image unavailable (${probe.status})`)\n}","typeGuard":"null","tryCatchPattern":"let lastErr\nfor (const delay of [0, 500, 2000]) {\n  try { return await normalizeImageSourceToPayload(url) }\n  catch (e) {\n    lastErr = e\n    if (!/Image request failed: 5\\d\\d/.test(e.message)) break\n    await new Promise(r => setTimeout(r, delay))\n  }\n}\nthrow lastErr","preventionTips":["Refresh signed URLs before fetching","Retry 5xx with backoff; fail fast on 4xx","Verify CORS and hotlink rules on the image host"],"tags":["http","network","image","fetch"],"backgroundTag":"http-request-failed","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}