remotion-dev/remotion · error · Error

Unsupported CTTS version

Error message

Unsupported CTTS version ${version}

What it means

The CTTS (Composition Time to Sample) box declares a version byte other than 0 or 1. Version 0 stores unsigned offsets and version 1 stores signed offsets; both are the only standardized versions. Any other value means the box is malformed or the parser is misaligned (read the wrong byte as the version).

Solutions

  1. Validate the file with mp4box/ffprobe: `ffprobe -v error -show_format input.mp4` — if ffprobe also errors, the file is corrupt.
  2. Check whether an earlier box in the same moov/traf threw a size-mismatch error; cursor desync is the most common root cause.
  3. Re-mux the file with `ffmpeg -i input.mp4 -c copy -movflags +faststart remuxed.mp4` to normalize box layout.
  4. If the source is a live stream or fragmented MP4, ensure the moof/traf chain is complete and not truncated mid-flight.

Example fix

// before: corrupted moov produces CTTS version 0xff
ffmpeg -i corrupt.mp4 -c copy fixed.mp4
// after: re-mux regates a well-formed CTTS v0/v1
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the CTTS box version byte before parsing
function isValidCttsVersion(versionByte: number): boolean {
  return versionByte === 0 || versionByte === 1;
}
// Read the version byte at the box's data offset and check before invoking parseMedia

Type guard

function isSupportedCttsVersion(v: number): v is 0 | 1 {
  return v === 0 || v === 1;
}

Try / catch

try {
  await parseMedia({src, fields: {dimensions: true}});
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Unsupported CTTS version')) {
    // file likely corrupt or cursor-desynced; re-mux or reject
  } else throw err;
}

Prevention

When it happens

Trigger: Calling parseCtts on a CTTS box where the first byte (version) is not 0 or 1. This usually means the iterator is reading garbage because an earlier box was parsed with the wrong size, pushing the cursor into the middle of unrelated data.

Common situations: Corrupt or truncated MP4 files, files produced by non-conformant encoders, or files where a preceding box parser consumed too many/few bytes and desynchronized the cursor. Rarely a genuinely novel version.

Related errors


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

Appendix: source

Thrown at packages/media-parser/src/containers/iso-base-media/stsd/ctts.ts:28

	type: 'ctts-box';
	version: number;
	flags: number[];
	entryCount: number;
	entries: CttsEntry[];
}

export const parseCtts = ({
	iterator,
	offset,
	size,
}: {
	iterator: BufferIterator;
	offset: number;
	size: number;
}): CttsBox => {
	const version = iterator.getUint8();
	if (version !== 0 && version !== 1) {
		throw new Error(`Unsupported CTTS version ${version}`);
	}

	const flags = iterator.getSlice(3);
	const entryCount = iterator.getUint32();

	const entries: CttsEntry[] = [];

	for (let i = 0; i < entryCount; i++) {
		const sampleCount = iterator.getUint32();

		// V1 = signed, V0 = unsigned
		// however some files are buggy

		// Let's do the same thing as mp4box
		// https://github.com/gpac/mp4box.js/blob/c6cc468145bc5b031b866446111f29c8b620dbe6/src/parsing/ctts.js#L2
		const sampleOffset = iterator.getInt32();

		entries.push({

View on GitHub (pinned to 78fe4bb3fd)