{"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":null,"httpStatus":null,"severity":"error","filePath":"server/utils/ImageGenerators/base.js","lineNumber":103,"sourceCode":"      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    let result;\n    if (image?.b64_json) {\n      result = { buffer: Buffer.from(image.b64_json, \"base64\") };\n    } else 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      result = { buffer: Buffer.from(await imgRes.arrayBuffer()) };\n    } else {\n      throw new Error(\"Image edit returned no image data.\");\n    }\n    this._sendImageTelemetry(\"image_generated\", {\n      withReferences: images.length > 0,\n    });\n    return result;\n  }\n\n  /**\n   * Emits a telemetry event if Telemetry is turned on.\n   * @param {string} event\n   * @param {Object} [additionalOpts]\n   */\n  _sendImageTelemetry(event, additionalOpts = {}) {\n    const { Telemetry } = require(\"../../models/telemetry\");\n    Telemetry.sendTelemetry(event, {","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/f92433b4ea0598492a1e6645ea22addbb4dd1287/server/utils/ImageGenerators/base.js#L85-L121","documentation":"In editImage(), when the provider replies with an image URL instead of base64, AnythingLLM downloads that URL with fetch and throws 'Failed to fetch edited image: <status>' on any non-2xx. The edit itself succeeded; only the follow-up retrieval of the produced image bytes failed.","triggerScenarios":"The provider's signed CDN URL expired before the fetch ran; the server cannot reach the image host (egress firewall, docker without external DNS); the CDN returns 403/429 for bot checks or hotlink protection; the image store evicted the object between edit and download.","commonSituations":"Short-lived signed URLs (Azure-style) with any processing delay; air-gapped or proxied deployments that whitelist only the API host but not its CDN.","solutions":["Retry the edit promptly — a fresh request yields a fresh URL.","Allow egress from the AnythingLLM host to the provider's CDN/image domain (proxy, firewall, DNS).","Prefer a model/settings that return b64_json inline (gpt-image-1 always does), skipping the download.","Map the status: 403/401 → link access problem, 404 → expired URL, 5xx → provider CDN incident."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"async function editWithRetry(generator, params, maxAttempts = 2) {\n  for (let i = 1; ; i++) {\n    try {\n      return await generator.editImage(params);\n    } catch (err) {\n      if (\n        err.message.startsWith(\"Failed to fetch edited image:\") &&\n        i < maxAttempts\n      ) {\n        continue; // a fresh edit gets a fresh, unexpired URL\n      }\n      throw err;\n    }\n  }\n}","preventionTips":["Ensure the server can reach the provider's CDN domain, not just its API host.","Prefer base64-returning models (gpt-image-1) to eliminate the second fetch.","Download promptly after generation — signed URLs are short-lived."],"tags":["image-generation","image-edit","download","http-status"],"backgroundTag":"image-download-failed","analyzedSha":"f92433b4ea0598492a1e6645ea22addbb4dd1287","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}