remotion-dev/remotion · error · Error
Image has not loaded
Error message
Image has not loaded
What it means
When copying an <img>-based frame to the clipboard, copyImageToClipboard checks image.naturalWidth/naturalHeight. Both are 0 until an image has finished loading (or if it failed), so the copy is refused with this error rather than producing a blank clipboard image.
Solutions
- Wait for the frame/thumbnail to finish loading (visible in the Timeline) before copying.
- Check the Network tab for a failed image request and fix the asset URL or connection.
- Retry the copy after the image loads; the natural size becomes non-zero once loading completes.
- If the image never loads, verify the public/ folder or remote host serving the composition assets.
Defensive patterns
Strategy: validation
Validate before calling
const img = el as HTMLImageElement;
if (img.tagName === 'IMG' && (img.naturalWidth === 0 || img.naturalHeight === 0)) {
await new Promise(r => (img.complete ? r(null) : img.addEventListener('load', r, {once: true})));
} Type guard
const imageLoaded = (img: HTMLImageElement): boolean => img.complete && img.naturalWidth > 0 && img.naturalHeight > 0;
Try / catch
try {
await copyImageToClipboard(imgEl);
} catch (e) {
if (e.message === 'Image has not loaded') {
// wait for load, then retry the copy
} else throw e;
} Prevention
- Wait for thumbnails/frames to render before invoking copy actions.
- Fix broken asset URLs flagged in the Network tab promptly.
- Preload images referenced by the composition before interactive editing.
When it happens
Trigger: Right-click > copy image on a Timeline frame backed by an <img> whose src has not finished loading, failed to load (404/network), or was removed from the DOM mid-action.
Common situations: Clicking copy immediately after scrubbing to a not-yet-loaded frame; broken remote image URLs; offline Studio usage; slow CDN responses.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot paste effects: no effects were copied
- A skill is already being installed or removed. Please try…
- Asset was not found in public/.
- assetPath must be a non-empty public-folder path, or an…
- Cannot create an undo entry without snapshots
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/6a4615c6d9ef1658.
Report an issue: GitHub.
Appendix: source
Thrown at packages/studio/src/components/Timeline/get-sequence-context-menu-items.ts:74
const copyImageToClipboard = async (element: Element): Promise<void> => {
let canvas: HTMLCanvasElement;
if (element.tagName === 'CANVAS') {
const sourceCanvas = element as HTMLCanvasElement;
canvas = document.createElement('canvas');
canvas.width = sourceCanvas.width;
canvas.height = sourceCanvas.height;
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Could not create canvas context');
}
context.drawImage(sourceCanvas, 0, 0);
} else if (element.tagName === 'IMG') {
const image = element as HTMLImageElement;
if (image.naturalWidth === 0 || image.naturalHeight === 0) {
throw new Error('Image has not loaded');
}
canvas = document.createElement('canvas');
canvas.width = image.naturalWidth;
canvas.height = image.naturalHeight;
const context = canvas.getContext('2d');
if (!context) {
throw new Error('Could not create canvas context');
}
context.drawImage(image, 0, 0);
} else if (element.tagName === 'VIDEO') {
const video = element as HTMLVideoElement;
if (video.videoWidth === 0 || video.videoHeight === 0) {
throw new Error('Video frame has not loaded');
}
canvas = document.createElement('canvas');View on GitHub (pinned to 46a3a6bf13)