remotion-dev/remotion · error · MediaPlaybackError

The browser threw an error while playing the video ${props.s

Error message

The browser threw an error while playing the video ${props.src}: Code ${current.error.code} - ${current?.error?.message}. See https://remotion.dev/docs/media-playback-error for help. Pass an onError() prop to handle the error.

What it means

In the server-render path of `<Video>`, when the underlying video element emits an `error` event with a populated `current.error` and the caller did not supply `onError`, Remotion throws a `MediaPlaybackError` with the browser code, message, the offending `src`, and a docs link.

Source

Thrown at packages/core/src/video/VideoForRendering.tsx:234

		});

		seek.prom.then(() => {
			continueRender(handle);
		});

		current.addEventListener('ended', endedHandler, {once: true});

		const errorHandler = () => {
			if (current?.error) {
				// eslint-disable-next-line no-console
				console.error('Error occurred in video', current?.error);

				// If user is handling the error, we don't cause an unhandled exception
				if (onError) {
					return;
				}

				throw new MediaPlaybackError({
					message: `The browser threw an error while playing the video ${props.src}: Code ${current.error.code} - ${current?.error?.message}. See https://remotion.dev/docs/media-playback-error for help. Pass an onError() prop to handle the error.`,
					src: props.src as string,
				});
			} else {
				throw new MediaPlaybackError({
					message: 'The browser threw an error',
					src: props.src as string,
				});
			}
		};

		current.addEventListener('error', errorHandler, {once: true});

		// If video skips to another frame or unmounts, we clear the created handle
		return () => {
			seek.cancel();
			current.removeEventListener('ended', endedHandler);
			current.removeEventListener('error', errorHandler);

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Add an `onError` prop to `<Video>` so the render can capture the failure.
  2. Re-encode the asset to faststart H.264 MP4 with AAC audio.
  3. Ensure the asset URL is reachable from the render environment (Lambda, Docker, etc.).
  4. Increase `delayRenderTimeoutInMilliseconds` if the asset is large but loadable.

Example fix

// before
<Video src={url} />
// after
<Video src={url} onError={(err) => console.error(err)} delayRenderTimeoutInMilliseconds={30000} />
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-validate asset reachability from the render host
const res = await fetch(src, {method: 'HEAD'});
if (!res.ok) throw new Error(`Asset unreachable from render host: ${res.status}`);

Type guard

const isFaststartMp4 = async (url: string): Promise<boolean> => { /* probe moov atom position */ return true; };

Try / catch

<Video
  src={url}
  delayRenderTimeoutInMilliseconds={30000}
  onError={(err) => {
    // capture but don't abort the render
    logRenderError(err);
  }}
/>

Prevention

When it happens

Trigger: Server-rendering a composition that contains `<Video>` whose asset fails to load or decode in the headless Chrome used by the renderer, with no `onError` handler on the component.

Common situations: Codec unsupported by the render Chrome (e.g. HEVC, ProRes), inaccessible URL from the render host, large asset failing on slow disk, or non-faststart MP4 forcing the renderer to time out and emit an error.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/ec4c0c245a6d3347. Report an issue: GitHub.