makeplane/plane · error · Error

Failed to fetch image: ${response.statusText}

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.

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)

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.