remotion-dev/remotion · error · Error

Expected an image, video, or canvas element

Error message

Expected an image, video, or canvas element

What it means

Thrown in copyImageToClipboard (a generic guard at the end of the element-dispatch chain): the function expects the timeline item thumbnail source to be an HTMLImageElement, HTMLCanvasElement, or HTMLVideoElement, but received an element with some other tagName. It fires when the caller passes an unsupported DOM element type to the clipboard-copy routine — the image and VIDEO branches have already been exhausted. This is an internal invariant violation rather than user input error; the caller should only pass elements produced from a sequence thumbnail that are one of the three supported media types.

Solutions

  1. Pass the actual <img>, <video>, or <canvas> node, not a wrapper element.
  2. Check element.tagName before calling and route unsupported types to a different handler.
  3. For arbitrary elements, first draw them into a canvas (e.g. via foreignObject/SVG serialization) and copy that canvas.
  4. Update Remotion Studio if a recent DOM refactor broke the element resolution.

Example fix

// before
await copyImageToClipboard(wrapperDiv); // throws
// after
const media = wrapperDiv.querySelector('img, video, canvas');
if (media) await copyImageToClipboard(media);
Defensive patterns

Strategy: type-guard

Validate before calling

if (!['IMG','VIDEO','CANVAS'].includes(el.tagName)) throw new TypeError('copyImageToClipboard requires img/video/canvas');

Type guard

const isCopyable = (el: Element): el is HTMLImageElement | HTMLVideoElement | HTMLCanvasElement =>
  el instanceof HTMLImageElement || el instanceof HTMLVideoElement || el instanceof HTMLCanvasElement;

Try / catch

try {
  await copyImageToClipboard(el);
} catch (err) {
  if (err.message.startsWith('Expected an image, video, or canvas')) {
    // resolve the actual media node before retrying
  }
}

Prevention

When it happens

Trigger: Invoking the copy-image action on a timeline thumbnail element that is not an <img>, <video>, or <canvas> — e.g. a <div>, <svg>, or other wrapper element was passed instead of the media node.

Common situations: DOM structure changes in Studio so the element resolved from the thumbnail no longer is a media element; custom code reusing copyImageToClipboard with an unsupported node.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: source

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

		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) {
				resolve(result);
			} else {
				reject(new Error('Could not convert image to PNG'));
			}
		}, 'image/png');
	});

	await navigator.clipboard.write([
		new ClipboardItem({
			'image/png': blob,
		}),
	]);
};

View on GitHub (pinned to 46a3a6bf13)