remotion-dev/remotion · error · Error

Video frame has not loaded

Error message

Video frame has not loaded

What it means

copyImageToClipboard draws the timeline thumbnail element into a canvas so it can be copied to the clipboard. For <video> elements it uses videoWidth/videoHeight as the canvas size; if the video has no decoded frame yet, both are 0 and drawing would fail, so the library throws this error upfront.

Solutions

  1. Wait for the video element's loadeddata/loadedmetadata (or seeked) event before copying.
  2. Check video.readyState >= 2 (HAVE_CURRENT_DATA) and videoWidth > 0 before invoking the copy action.
  3. Retry the copy after a short delay or after scrubbing the timeline to force frame decode.
  4. If the source is broken, fix the video URL/source so a frame can load.

Example fix

// before
await copyImageToClipboard(videoEl); // may throw
// after
if (videoEl.readyState < 2 || videoEl.videoWidth === 0) {
  await new Promise((r) => videoEl.addEventListener('loadeddata', r, {once: true}));
}
await copyImageToClipboard(videoEl);
Defensive patterns

Strategy: validation

Validate before calling

function canCopyVideoFrame(v) {
  return v instanceof HTMLVideoElement && v.readyState >= 2 && v.videoWidth > 0 && v.videoHeight > 0;
}

Type guard

const isVideoWithFrame = (el: Element): el is HTMLVideoElement =>
  el instanceof HTMLVideoElement && el.videoWidth > 0 && el.videoHeight > 0;

Try / catch

try {
  await copyImageToClipboard(el);
} catch (err) {
  if (err.message === 'Video frame has not loaded') {
    // retry after loadeddata or show a 'frame not ready' toast
  }
}

Prevention

When it happens

Trigger: Right-clicking 'Copy image to clipboard' on a Timeline/Sequence thumbnail whose <video> element has not finished loading or decoding any frame (videoWidth === 0 || videoHeight === 0).

Common situations: Copying a video element immediately after page load or seek before metadata/first frame is ready; slow network so the video has not buffered; a broken or unsupported video source so the frame never loads.

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


AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18). Data as JSON: /api/errors/308c75d4953569e7. Report an issue: GitHub.

Appendix: source

Thrown at packages/studio/src/components/Timeline/get-sequence-context-menu-items.ts:89

	} 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');
		canvas.width = video.videoWidth;
		canvas.height = video.videoHeight;
		const context = canvas.getContext('2d');
		if (!context) {
			throw new Error('Could not create canvas context');
		}

		context.drawImage(video, 0, 0);
	} else {
		throw new Error('Expected an image, video, or canvas element');
	}

	const blob = new Promise<Blob>((resolve, reject) => {
		canvas.toBlob((result) => {
			if (result) {

View on GitHub (pinned to 46a3a6bf13)