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
- Inspect response.status (surfaced only as statusText here) — log the numeric code for diagnosis.
- For 403/410 on signed URLs, refresh the URL and retry once.
- For 404, fall back to a default image rather than throwing.
- 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
- Refresh expired signed URLs before fetching
- Fall back to a default image on 404/410
- Retry once on transient 5xx
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
- Invalid URL provided
- Invalid URL format
- Failed to fetch image: ${response.statusText}
- 5115|5120|5121|5123|5104
- Invalid schema. Only HTTP and HTTPS are allowed.
AI-assisted analysis of makeplane/plane@1c8a60f858 (2026-08-12).
Data as JSON: /api/errors/e9cdc04b66f312a9.
Report an issue: GitHub.