remotion-dev/remotion · error · Error

Expected tkhd box in trak box

Error message

Expected tkhd box in trak box

What it means

Thrown by makeBaseMediaTrack when getTkhdBox(trakBox) returns null during track construction. The tkhd (Track Header) box carries trackId, dimensions, and timing — all required to materialize a MediaParserTrack. ISO/IEC 14496-12 mandates tkhd in every trak, so a missing tkhd indicates a malformed file or a parsing bug that misclassified the box.

Source

Thrown at packages/media-parser/src/containers/iso-base-media/make-track.ts:54

import {findTrackMediaTimeOffsetInTrackTimescale} from './mdat/get-editlist';
import type {TrakBox} from './trak/trak';
import {getTkhdBox, getVideoDescriptors} from './traversal';

export const makeBaseMediaTrack = (
	trakBox: TrakBox,
	startTimeInSeconds: number,
):
	| MediaParserVideoTrack
	| MediaParserAudioTrack
	| MediaParserOtherTrack
	| null => {
	const tkhdBox = getTkhdBox(trakBox);

	const videoDescriptors = getVideoDescriptors(trakBox);
	const timescaleAndDuration = getTimescaleAndDuration(trakBox);

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

	if (!timescaleAndDuration) {
		throw new Error('Expected timescale and duration in trak box');
	}

	if (trakBoxContainsAudio(trakBox)) {
		const numberOfChannels = getNumberOfChannelsFromTrak(trakBox);
		if (numberOfChannels === null) {
			throw new Error('Could not find number of channels');
		}

		const sampleRate = getSampleRate(trakBox);
		if (sampleRate === null) {
			throw new Error('Could not find sample rate');
		}

		const {codecString, description} = getAudioCodecStringFromTrak(trakBox);

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Re-mux with `ffmpeg -i in.mp4 -c copy -movflags faststart out.mp4` to regenerate tkhd.
  2. Validate the file with `ffprobe -show_streams`; reject files that report a malformed track header.
  3. Catch the error around the parse call and present a 'corrupt file' message to the user.
  4. If authoring traks programmatically, ensure tkhd is the first child of every trak.

Example fix

// before
await parseMedia({ src: file });

// after
try {
  await parseMedia({ src: file });
} catch (err) {
  if (/Expected tkhd box/i.test(String(err?.message))) {
    throw new Error('MP4 is missing a required track header (tkhd). The file is corrupt; re-mux it with ffmpeg.');
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

import type {TrakBox} from './trak/trak';
import {getTkhdBox} from './traversal';
function trakHasTkhdForTrack(trakBox: TrakBox): boolean {
  return getTkhdBox(trakBox) !== null;
}

Type guard

import type {TrakBox} from './trak/trak';
import type {TkhdBox} from './tkhd';
import {getTkhdBox} from './traversal';
function hasTkhdBox(trakBox: TrakBox): boolean {
  return getTkhdBox(trakBox) !== null;
}

Try / catch

try {
  await parseMedia({ src: file });
} catch (err) {
  if (/Expected tkhd box in trak box/i.test(String(err?.message))) {
    throw new Error('MP4 track is missing its header (tkhd). The file is corrupt; re-mux with ffmpeg -movflags faststart.');
  }
  throw err;
}

Prevention

When it happens

Trigger: Reached for every trak in the moov during the initial track-registration pass. Fires when a trak has no tkhd-box child, or when the tkhd was parsed under a different type due to version/flag misreading. Distinct from the seeking-path tkhd errors because this blocks track creation entirely.

Common situations: Corrupt moov atoms. Files from non-standard muxers. Truncated uploads where moov is partially present. Test fixtures with synthetic traks missing tkhd.

Related errors


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