remotion-dev/remotion · error · Error

Expected stts box in trak box

Error message

Expected stts box in trak box

What it means

Thrown by collectSamplePositionsFromTrak when a trak box has no stts (Decoding Time-to-Sample) box. stts is the table that maps each sample to its decoding timestamp delta, the basis for every timing calculation in the track. ISO/IEC 14496-12 requires it for any track that carries real samples; without it the parser cannot reconstruct timestamps.

Source

Thrown at packages/media-parser/src/containers/iso-base-media/collect-sample-positions-from-trak.ts:46

	const stscBox = getStscBox(trakBox);
	const stssBox = getStssBox(trakBox);
	const sttsBox = getSttsBox(trakBox);
	const cttsBox = getCttsBox(trakBox);

	if (!stszBox) {
		throw new Error('Expected stsz box in trak box');
	}

	if (!stcoBox) {
		throw new Error('Expected stco box in trak box');
	}

	if (!stscBox) {
		throw new Error('Expected stsc box in trak box');
	}

	if (!sttsBox) {
		throw new Error('Expected stts box in trak box');
	}

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

	const samplePositions = getSamplePositions({
		stcoBox,
		stscBox,
		stszBox,
		stssBox,
		sttsBox,
		cttsBox,
	});

	return samplePositions;
};

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Re-mux with ffmpeg `-c copy -movflags faststart` to regenerate stts.
  2. Run `ffprobe` on the source and reject files whose streams report errors.
  3. Catch the error at the parse boundary and present a 'corrupt or unsupported file' message.
  4. If the source is a stream you control, ensure the upstream encoder writes a complete sample table.

Example fix

// before
await parseMedia({ src, fields: { samplePositions: true } });

// after
try {
  await parseMedia({ src, fields: { samplePositions: true } });
} catch (err) {
  if (String(err?.message).startsWith('Expected st')) {
    throw new Error('MP4 sample table is incomplete. Re-mux the file with ffmpeg -movflags faststart.');
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

import {execFileSync} from 'node:child_process';
function hasValidTiming(file: string): boolean {
  try {
    const out = execFileSync('ffprobe', ['-v', 'error', '-show_entries', 'stream=codec_type,duration', '-of', 'json', file], {encoding: 'utf8'});
    const data = JSON.parse(out);
    return data.streams?.every((s: any) => typeof s.duration === 'string');
  } catch { return false; }
}

Type guard

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

Try / catch

try {
  const positions = collectSamplePositionsFromTrak(trakBox);
} catch (err) {
  if (/Expected stts box/i.test(String(err?.message))) {
    return { seekable: false, reason: 'missing-stts' };
  }
  throw err;
}

Prevention

When it happens

Trigger: A progressive MP4 trak whose stbl lacks stts. Reachable for traks authored without a time table, or when a truncation/copy fault dropped the box. The check fires after stsz, stco, and stsc have already validated, so this is the fourth mandatory table the parser needs.

Common situations: Half-downloaded or partially uploaded MP4s. Files produced by experimental or buggy muxers. Tracks repackaged from transport streams (.ts) into MP4 incorrectly. Damaged media in a CDN cache.

Related errors


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