{"record":{"id":"f5d54c42207bacfa","repo":"Mintplex-Labs/anything-llm","slug":"failed-to-fetch-edited-image-imgres-status","errorCode":null,"errorMessage":"Failed to fetch edited image: ${imgRes.status}","messagePattern":"Failed to fetch edited image: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/utils/ImageGenerators/base.js","lineNumber":109,"sourceCode":"      body: formData,\n      signal: signal ?? null,\n    });\n\n    if (!res.ok) {\n      const body = await res.text().catch(() => \"\");\n      throw new Error(\n        `Image edit failed (${res.status}): ${body || res.statusText}`\n      );\n    }\n\n    const payload = await res.json();\n    const image = payload?.data?.[0];\n    if (image?.b64_json)\n      return { buffer: Buffer.from(image.b64_json, \"base64\") };\n    if (image?.url) {\n      const imgRes = await fetch(image.url, { signal: signal ?? null });\n      if (!imgRes.ok)\n        throw new Error(`Failed to fetch edited image: ${imgRes.status}`);\n      return { buffer: Buffer.from(await imgRes.arrayBuffer()) };\n    }\n    throw new Error(\"Image edit returned no image data.\");\n  }\n\n  async requestImage(prompt, size, signal) {\n    this.log(`Generating ${size} image with ${this.model}.`);\n    const result = await this.client.images.generate(\n      {\n        model: this.model,\n        prompt,\n        size,\n        n: 1,\n      },\n      { signal: signal ?? undefined }\n    );\n\n    // Some OpenAI-compatible providers (e.g. Ollama) return the body with a","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/526360e320da9d1b36074be5ed64fe76e5bbfbbd/server/utils/ImageGenerators/base.js#L91-L127","documentation":"Thrown in editImage after a successful /images/edits call that returned a URL instead of base64. The code fetches that image URL (passing through the AbortSignal) and, if the fetch is not ok, throws only the numeric HTTP status. This is the download stage: the edit succeeded but the hosted result image could not be retrieved.","triggerScenarios":"The provider returns a signed/temporary URL for the edited image, but fetching it returns non-2xx: expired pre-signed URL, provider CDN outage, region-restricted storage, 403 because the URL's token expired, or the signal aborted but surfaced as a status error.","commonSituations":"Providers that return URLs (not base64) where the link is short-lived and expires between edit completion and download; network egress restrictions in the deployment blocking the image-hosting domain; the download fetch raced with a timeout/abort.","solutions":["Retry the edit/download — short-lived URL expiry is often transient.","Check the deployment's network egress allows the image-hosting domain returned by the provider.","If the provider supports response_format=b64_json, prefer it to avoid the second fetch entirely.","Increase the request timeout / avoid aborting the signal before the download completes."],"exampleFix":"// before\nconst imgRes = await fetch(image.url, { signal: signal ?? null });\nif (!imgRes.ok) throw new Error(`Failed to fetch edited image: ${imgRes.status}`);\n\n// after: retry with backoff for transient download failures\nasync function fetchWithRetry(url, signal, tries = 3) {\n  for (let i = 0; i < tries; i++) {\n    const r = await fetch(url, { signal: signal ?? null });\n    if (r.ok) return r;\n    if (r.status < 500 || i === tries - 1) throw new Error(`Failed to fetch edited image: ${r.status}`);\n    await new Promise(res => setTimeout(res, 500 * (i + 1)));\n  }\n}","handlingStrategy":"retry","validationCode":"// Cannot validate a remote URL's availability before the edit returns it,\n// but you can ensure network egress is allowed:\nasync function canReachHost(urlString) {\n  try { return new URL(urlString).hostname.length > 0; } catch { return false; }\n}","typeGuard":"/** @param {unknown} e */\nfunction isEditedImageFetchError(e) {\n  return e instanceof Error && /^Failed to fetch edited image: \\d+$/.test(e.message);\n}","tryCatchPattern":"// Retry transient (5xx) download failures with backoff\nasync function downloadEditedImage(url, signal) {\n  for (let i = 0; i < 3; i++) {\n    const r = await fetch(url, { signal: signal ?? null });\n    if (r.ok) return Buffer.from(await r.arrayBuffer());\n    if (r.status < 500) throw new Error(`Failed to fetch edited image: ${r.status}`);\n    await new Promise(res => setTimeout(res, 500 * (i + 1)));\n  }\n  throw new Error('Edited image download failed after retries');\n}","preventionTips":["Prefer providers that return b64_json to skip the download step entirely.","Allow egress to the provider's image-hosting domain in your network policy.","Avoid aborting the signal before the download completes.","Retry 5xx download failures; surface 4xx as permanent."],"tags":["image-generation","image-edit","download","network","transient"],"backgroundTag":null,"analyzedSha":"526360e320da9d1b36074be5ed64fe76e5bbfbbd","analyzedAt":"2026-08-13T01:45:47.170Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}