linshenkx/prompt-optimizer · error · Error

Image request failed: ${resp.status}

Error message

Image request failed: ${resp.status}

What it means

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.

Source

Thrown at packages/ui/src/utils/image-asset-storage.ts:84

  if (
    bytes.length >= 6 &&
    bytes[0] === 0x47 &&
    bytes[1] === 0x49 &&
    bytes[2] === 0x46 &&
    bytes[3] === 0x38 &&
    (bytes[4] === 0x37 || bytes[4] === 0x39) &&
    bytes[5] === 0x61
  ) {
    return 'image/gif'
  }

  return null
}

const fetchImagePayloadFromUrl = async (absoluteUrl: string): Promise<ImagePayload> => {
  const resp = await fetch(absoluteUrl, { method: 'GET' })
  if (!resp.ok) {
    throw new Error(`Image request failed: ${resp.status}`)
  }

  const headerType = resp.headers.get('content-type')
  const mimeType = typeof headerType === 'string' ? headerType.split(';')[0].trim() : ''
  const ab = await resp.arrayBuffer()
  const bytes = new Uint8Array(ab)
  const inferredMimeType = inferMimeTypeFromBytes(bytes)
  const finalMimeType =
    mimeType && mimeType !== 'application/octet-stream'
      ? mimeType
      : inferredMimeType || mimeType || 'application/octet-stream'

  type BufferLike = {
    from: (data: ArrayBuffer) => { toString: (encoding: 'base64') => string }
  }

  const maybeBuffer = (globalThis as unknown as { Buffer?: BufferLike }).Buffer
  if (maybeBuffer && typeof maybeBuffer.from === 'function') {

View on GitHub (pinned to 3e677b1d9f)

Solutions

  1. Check the status in the message: 404 means the URL is wrong/deleted, 403 means auth/hotlink protection, 5xx means retry later
  2. If using signed URLs, generate a fresh signed URL before fetching
  3. For transient 5xx, retry with backoff or fall back to a placeholder
  4. Verify the URL opens in a browser/incognito and that CORS headers allow your origin

Example fix

// before
const payload = await normalizeImageSourceToPayload(url)

// after
let payload
try {
  payload = await normalizeImageSourceToPayload(url)
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Image request failed:')) {
    payload = PLACEHOLDER_PAYLOAD // or re-sign URL and retry
  } else throw e
}
Defensive patterns

Strategy: retry

Validate before calling

const probe = await fetch(url, { method: 'HEAD' })
if (!probe.ok) {
  if (probe.status === 403 || probe.status === 401) await refreshSignedUrl()
  else throw new Error(`Image unavailable (${probe.status})`)
}

Type guard

null

Try / catch

let lastErr
for (const delay of [0, 500, 2000]) {
  try { return await normalizeImageSourceToPayload(url) }
  catch (e) {
    lastErr = e
    if (!/Image request failed: 5\d\d/.test(e.message)) break
    await new Promise(r => setTimeout(r, delay))
  }
}
throw lastErr

Prevention

When it happens

Trigger: 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.

Common situations: Expired S3/CDN signed URLs; hotlink-protected image hosts returning 403; deleted attachments; server briefly returning 502/503; typo'd URL paths.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/20e2df6fe0cb8700. Report an issue: GitHub.