remotion-dev/remotion · error · Error
getVideoMetadata() is only available in the browser.
Error message
getVideoMetadata() is only available in the browser.
What it means
Thrown by getVideoMetadata() when `document` is undefined (line 17). The implementation builds a <video> element via document.createElement and reads loadedmetadata/duration, so it cannot run outside a browser DOM.
Source
Thrown at packages/media-utils/src/get-video-metadata.ts:17
/* eslint-disable @typescript-eslint/no-use-before-define */
import {isRemoteAsset} from './is-remote-asset';
import {onMediaError} from './media-tag-error-handling';
import {pLimit} from './p-limit';
import type {VideoMetadata} from './types';
const cache: {[key: string]: VideoMetadata} = {};
const limit = pLimit(3);
const fn = (src: string): Promise<VideoMetadata> => {
if (cache[src]) {
return Promise.resolve(cache[src]);
}
if (typeof document === 'undefined') {
throw new Error('getVideoMetadata() is only available in the browser.');
}
const video = document.createElement('video');
video.src = src;
return new Promise<VideoMetadata>((resolve, reject) => {
const onError = () => {
onMediaError({
error: video.error!,
src,
cleanup,
reject,
api: 'getVideoMetadata()',
});
};
const onLoadedMetadata = () => {
const pixels = video.videoHeight * video.videoWidth;
View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Call getVideoMetadata() only from Remotion components or other browser code paths.
- For server-side metadata, use Mediabunny's metadata API or ffprobe, which do not require a DOM.
- In tests, run under jsdom/happy-dom or use the Remotion browser testbed; do not invoke under plain Node.
- Guard with typeof document !== 'undefined' before calling during any isomorphic code path.
Example fix
// before (server call throws)
import {getVideoMetadata} from '@remotion/media-utils';
const meta = await getVideoMetadata('/clip.mp4');
// after (client-side via a component effect)
import {getVideoMetadata} from '@remotion/media-utils';
useEffect(() => {
if (typeof document === 'undefined') return;
getVideoMetadata(staticFile('clip.mp4')).then(setMeta);
}, []); Defensive patterns
Strategy: validation
Validate before calling
import {getVideoMetadata} from '@remotion/media-utils';
async function safeGetVideoMetadata(src: string) {
if (typeof document === 'undefined') {
return null; // caller should fall back to ffprobe / Mediabunny metadata
}
return getVideoMetadata(src);
} Type guard
const canUseDomVideo = (): boolean => typeof document !== 'undefined';
Try / catch
try {
const meta = await getVideoMetadata(src);
} catch (err) {
if ((err as Error).message.includes('only available in the browser')) {
// server path: use ffprobe / Mediabunny
} else {
throw err;
}
} Prevention
- Call getVideoMetadata only from Remotion client components.
- For server-side metadata use ffprobe or Mediabunny's metadata API.
- Guard any isomorphic call site with typeof document !== 'undefined'.
- Do not run this in build scripts or serverless functions that lack a DOM.
When it happens
Trigger: Calling getVideoMetadata() in SSR, in Node scripts, in Node test runners without a DOM polyfill, or in worker/edge runtimes where document is not defined.
Common situations: Pre-rendering a video gallery server-side; Next.js server component fetching video metadata; CI/build-time asset validation; importing the helper into a build script that runs under Node.
Related errors
- getAudioData() is only available in the browser.
- getAudioDuration() is only available in the browser.
- getImageDimensions() is only available in the browser.
- Tried to call an API that only works in the browser from out
- measureText() can only be called in a browser.
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/b58556e1283ea6e3.
Report an issue: GitHub.