makeplane/plane · error · Error

Failed to fetch image

Error message

Failed to fetch image: ${response.statusText}

What it means

Third guard in getBase64Image: after a syntactically valid URL is fetched, response.ok must be true (2xx). Any non-2xx status (404 missing file, 403 forbidden, 500 server error, 502/504 gateway) surfaces as this error with the HTTP statusText. The fetch itself succeeded at the transport level but the server returned an error status.

Solutions

  1. Inspect response.status (surfaced only as statusText here) — log the numeric code for diagnosis.
  2. For 403/410 on signed URLs, refresh the URL and retry once.
  3. For 404, fall back to a default image rather than throwing.
  4. For 5xx, implement a single retry with backoff.

Example fix

// before
const b64 = await getBase64Image(url);

// after
let b64;
try {
  b64 = await getBase64Image(url);
} catch (e) {
  // statusText in message; fall back to default avatar
  b64 = DEFAULT_AVATAR_B64;
}
Defensive patterns

Strategy: fallback

Validate before calling

async function fetchOk(url: string): Promise<Response> {
  const r = await fetch(url);
  if (!r.ok) throw new Error(`status ${r.status}`);
  return r;
}

Try / catch

try { b64 = await getBase64Image(url); } catch (e) { if (/Failed to fetch image/.test((e as Error).message)) { b64 = DEFAULT_AVATAR_B64; } else throw e; }

Prevention

When it happens

Trigger: 404 when the avatar/cover file was deleted; 403 when the CDN token expired or CORS policy blocks; 500/502/504 from a failing upstream; 451/410 for removed content.

Common situations: Stale avatar URL after re-upload; expired pre-signed URL; rate limiting (429); CORS or auth header issues manifesting as a non-2xx response.

Related errors


AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12). Data as JSON: /api/errors/e9cdc04b66f312a9. Report an issue: GitHub.

Appendix: source

Thrown at packages/utils/src/file.ts:55

 * @param {string} url
 * @returns
 */
export const getBase64Image = async (url: string): Promise<string> => {
  if (!url || typeof url !== "string") {
    throw new Error("Invalid URL provided");
  }

  // Try to create a URL object to validate the URL
  try {
    new URL(url);
  } catch {
    throw new Error("Invalid URL format");
  }

  const response = await fetch(url);
  // check if the response is OK
  if (!response.ok) {
    throw new Error(`Failed to fetch image: ${response.statusText}`);
  }

  const blob = await response.blob();
  return new Promise((resolve, reject) => {
    const reader = new FileReader();

    reader.onloadend = () => {
      if (reader.result) {
        resolve(reader.result as string);
      } else {
        reject(new Error("Failed to convert image to base64."));
      }
    };

    reader.onerror = () => {
      reject(new Error("Failed to read the image file."));
    };

View on GitHub (pinned to 1c8a60f858)