remotion-dev/remotion · error · Error

No moov atom found

Error message

No moov atom found

What it means

Thrown by getSeekingByteFromFragmentedMp4 when getMoovBoxFromState returns null while resolving a fragmented MP4 seek. Unlike the progressive path, fragmented MP4 keeps moov in parser state (and optionally the precomputed cache or mp4HeaderSegment); if none of those sources has it yet, the seek cannot be resolved. This is usually a timing/ordering issue rather than file corruption.

Source

Thrown at packages/media-parser/src/containers/iso-base-media/get-seeking-byte-from-fragmented-mp4.ts:62

	mp4HeaderSegment: IsoBaseMediaStructure | null;
}): Promise<SeekResolution> => {
	const firstVideoTrack = tracks.find((t) => t.type === 'video');

	// If there is both video and audio, seek based on video, but if not then audio is also okay
	const firstTrack = firstVideoTrack ?? tracks.find((t) => t.type === 'audio');

	if (!firstTrack) {
		throw new Error('no video and no audio tracks');
	}

	const moov = getMoovBoxFromState({
		structureState: structure,
		isoState,
		mp4HeaderSegment,
		mayUsePrecomputed: true,
	});
	if (!moov) {
		throw new Error('No moov atom found');
	}

	const trakBox = getTrakBoxByTrackId(moov, firstTrack.trackId);

	if (!trakBox) {
		throw new Error('No trak box found');
	}

	const tkhdBox = getTkhdBox(trakBox);
	if (!tkhdBox) {
		throw new Error('Expected tkhd box in trak box');
	}

	const isComplete = areSamplesComplete({
		moofBoxes: info.moofBoxes,
		tfraBoxes: info.tfraBoxes,
	});

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Wait for the parser to emit its first track (onVideoTrack/onAudioTrack) before seeking — that guarantees moov is loaded.
  2. If using an m3u8 source, ensure the init segment (#EXT-X-MAP) is reachable.
  3. Retry the seek after a short delay or on the next progress event; this is often transient.
  4. If persistent, validate the source exposes moov in its init segment via `ffprobe`.

Example fix

// before
await parser.seek(time);

// after
let moovReady = false;
await parseMedia({
  src,
  onVideoTrack: () => { moovReady = true; },
  onAudioTrack: () => { moovReady = true; },
});
if (!moovReady) throw new Error('Cannot seek: moov not loaded yet');
await parser.seek(time);
Defensive patterns

Strategy: retry

Validate before calling

// Track when moov becomes available via the first onVideoTrack/onAudioTrack callback
let moovReady = false;
await parseMedia({ src, onVideoTrack: () => { moovReady = true; }, onAudioTrack: () => { moovReady = true; } });
if (!moovReady) throw new Error('moov not loaded');

Type guard

import type {StructureState} from '../../state/structure';
import type {IsoBaseMediaState} from '../../state/iso-base-media/iso-state';
import {getMoovBoxFromState} from './traversal';
function moovIsAvailable(structure: StructureState, iso: IsoBaseMediaState, header: any): boolean {
  return getMoovBoxFromState({ structureState: structure, isoState: iso, mp4HeaderSegment: header, mayUsePrecomputed: true }) !== null;
}

Try / catch

try {
  await parser.seek(time);
} catch (err) {
  if (/No moov atom found/i.test(String(err?.message))) {
    // transient: retry after the next progress event
    parser.once('progress', () => parser.seek(time));
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Seek is requested before the moov atom has been parsed into state. Reachable when the m3u8 init segment has not been fully processed, when precomputed moov is disallowed (mayUsePrecomputed=false in some other call path), or when the moov fetch is still in flight. The check fires after confirming a track exists but before extracting its trak.

Common situations: Calling seek too early in a streaming/fMP4 parse. Init segment still downloading. A reader that delivers fragments before the init. State not yet populated because the moov-bearing box was just queued.

Related errors


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