remotion-dev/remotion · error · Error
Unsupported CTTS version ${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).
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)
Solutions
- Validate the file with mp4box/ffprobe: `ffprobe -v error -show_format input.mp4` — if ffprobe also errors, the file is corrupt.
- Check whether an earlier box in the same moov/traf threw a size-mismatch error; cursor desync is the most common root cause.
- Re-mux the file with `ffmpeg -i input.mp4 -c copy -movflags +faststart remuxed.mp4` to normalize box layout.
- 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
- Pre-validate uploads with ffprobe before parsing.
- Treat any 'Unsupported X version' error as a symptom and inspect earlier boxes for the real cause.
- Keep uncorrupted backups so re-muxing is always an option.
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
- Expected stsz box in trak box
- Expected stco box in trak box
- Expected stsc box in trak box
- Expected stts box in trak box
- Expected timescale and duration in trak box
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/457d975040357bd5.
Report an issue: GitHub.