AykutSarac/jsoncrack.com · error
Failed to download image!
Error message
Failed to download image!
What it means
Generic toast from exportAsImage()'s catch when html-to-image (toSvg/toPng/toJpeg) throws. Covers rendering failures: the target node contains cross-origin images/canvas that taint the output (SecurityError), a font/image fails to load, the element is too large causing memory/timeouts, or getDownloadFormat returns undefined for an unknown extension.
Source
Thrown at apps/www/src/features/modals/DownloadModal/index.tsx:138
toast.loading("Downloading...", { id: "toastDownload" });
const imageElement = getExportElement();
if (!imageElement) {
toast.error("Canvas not found.");
return;
}
const imageOptions = {
quality: fileDetails.quality,
backgroundColor: fileDetails.backgroundColor,
skipFonts: true,
};
const dataURI = await getDownloadFormat(extension)(imageElement, imageOptions);
downloadURI(dataURI, `${fileDetails.filename}.${extension}`);
gaEvent("download_img", { label: extension });
} catch {
toast.error("Failed to download image!");
} finally {
toast.dismiss("toastDownload");
onClose();
}
};
const updateDetails = (key: keyof typeof fileDetails, value: string | number) =>
setFileDetails({ ...fileDetails, [key]: value });
return (
<Modal opened={opened} onClose={onClose} title="Download Image" centered>
<TextInput
label="File Name"
value={fileDetails.filename}
onChange={e => updateDetails("filename", e.target.value)}
mb="lg"
/>
<SegmentedControlView on GitHub (pinned to 3c9af69e23)
Solutions
- Ensure all images/fonts rendered in the canvas are same-origin or CORS-enabled.
- Keep skipFonts:true (already set) to reduce font-load failures.
- Validate the selected extension is one of SVG/PNG/JPEG before calling getDownloadFormat.
- Log the caught error to identify the exact html-to-image failure (currently swallowed).
Example fix
// before
const dataURI = await getDownloadFormat(extension)(imageElement, imageOptions);
// ...
} catch {
toast.error("Failed to download image!");
}
// after — guard the format lookup and log the real cause
const renderer = getDownloadFormat(extension);
if (!renderer) { toast.error("Unknown format."); return; }
try {
const dataURI = await renderer(imageElement, imageOptions);
downloadURI(dataURI, `${fileDetails.filename}.${extension}`);
} catch (err) {
console.error(err);
toast.error("Failed to download image!");
} Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the extension maps to a renderer and content is CORS-safe
const KNOWN = new Set(["svg", "png", "jpeg"]);
export function canExport(ext: string): boolean {
return KNOWN.has(ext);
} Type guard
// Guard getDownloadFormat's return
function hasRenderer(fn: unknown): fn is (el: HTMLElement, opts: unknown) => Promise<string> {
return typeof fn === "function";
} Try / catch
// Guard format lookup, log the html-to-image cause
const renderer = getDownloadFormat(extension);
if (!hasRenderer(renderer)) { toast.error("Unknown format."); return; }
try { await renderer(el, opts); } catch (err) { console.error(err); toast.error("Failed to download image!"); } Prevention
- Ensure canvas images are same-origin or CORS-enabled to avoid taint.
- Keep skipFonts:true to reduce font-load failures.
- Validate the extension is SVG/PNG/JPEG before calling the renderer.
When it happens
Trigger: A graph referencing cross-origin images without CORS; a very large canvas triggering OOM; skipFonts is true but foreignObject still hits a tainted canvas; an extension value outside the SVG/PNG/JPEG enum making getDownloadFormat return undefined (then calling undefined() throws TypeError).
Common situations: Exporting large graphs; embedded remote images without CORS headers; Safari foreignObject quirks; runtime extension value drift.
Related errors
- Canvas not found.
- Failed to copy to clipboard
- Failed to fetch JSON!
- Clipboard write permission denied. Please allow clipboard ac
- Failed to fetch document from URL!
AI-assisted analysis of AykutSarac/jsoncrack.com@3c9af69e23 (2026-08-12).
Data as JSON: /api/errors/545110a12526d00c.
Report an issue: GitHub.