remotion-dev/remotion · error · TypeError

The `<Html5Audio>` tag requires a string for `src`, but got

Error message

The `<Html5Audio>` tag requires a string for `src`, but got ${JSON.stringify(props.src)} instead.

What it means

Html5Audio requires its src prop to be a plain string (a URL or static import path) so Remotion can preload, resolve, and track the media duration. Non-string values (undefined, number, object, React element) are rejected because the underlying <audio> tag and the duration-tracking context both depend on a resolvable string URL.

Source

Thrown at packages/core/src/audio/html5-audio.tsx:68

	const {fps} = useVideoConfig();
	const environment = useRemotionEnvironment();
	const shouldPauseWhenBuffering = resolveV5Default(pauseWhenBuffering);

	if (environment.isClientSideRendering) {
		throw new Error(
			'<Html5Audio> is not supported in @remotion/web-renderer. Use <Audio> from @remotion/media instead. See https://remotion.dev/docs/client-side-rendering/limitations',
		);
	}

	if (typeof freeze !== 'undefined') {
		throw new TypeError(
			'The "freeze" prop is not supported on <Html5Audio />. Use <Sequence freeze={...}> to freeze media playback.',
		);
	}

	const {durations, setDurations} = useContext(DurationsContext);
	if (typeof props.src !== 'string') {
		throw new TypeError(
			`The \`<Html5Audio>\` tag requires a string for \`src\`, but got ${JSON.stringify(
				props.src,
			)} instead.`,
		);
	}

	const preloadedSrc = usePreload(props.src);

	const onError: React.ReactEventHandler<HTMLAudioElement> = useCallback(
		(e) => {
			// eslint-disable-next-line no-console
			console.log(e.currentTarget.error);

			// If there is no `loop` property, we don't need to get the duration
			// and this does not need to be a fatal error
			const errMessage = `Could not play audio with src ${preloadedSrc}: ${e.currentTarget.error}. See https://remotion.dev/docs/media-playback-error for help.`;

			if (loop) {

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Ensure src is always a string: <Html5Audio src={typeof src === 'string' ? src : ''} /> or guard rendering with {src && <Html5Audio src={src} />}.
  2. If loading asynchronously, delay mounting Html5Audio until the URL is resolved (use delayRender/continueRender or a conditional render).
  3. Pass staticFile('audio.mp3') or a literal URL string directly.

Example fix

// before
<Html5Audio src={data?.audio} />

// after
{data?.audio ? <Html5Audio src={data.audio} /> : null}
Defensive patterns

Strategy: validation

Validate before calling

if (typeof src !== 'string' || src.length === 0) {
  // skip mounting or provide a default URL
  return null;
}

Type guard

const isValidAudioSrc = (src: unknown): src is string =>
  typeof src === 'string' && src.length > 0;

Prevention

When it happens

Trigger: Passing <Html5Audio src={undefined} />, src={0}, src={{url}}, src={<Img .../>}, or a conditional that evaluates to undefined/false. Also triggered by passing a staticFile() result wrapped in an array or object, or by forgetting to pass src entirely.

Common situations: Forgetting the src prop; conditional src that short-circuits to undefined when data hasn't loaded; migrating from <Audio> where src typing was looser; passing a remote object instead of its URL field.

Related errors


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