{"record":{"id":"a1eb8cd2fe2fba40","repo":"remotion-dev/remotion","slug":"expected-timescale-and-duration-in-trak-box","errorCode":null,"errorMessage":"Expected timescale and duration in trak box","messagePattern":"Expected timescale and duration in trak box","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media-parser/src/containers/iso-base-media/collect-sample-positions-from-trak.ts","lineNumber":50,"sourceCode":"\n\tif (!stszBox) {\n\t\tthrow new Error('Expected stsz box in trak box');\n\t}\n\n\tif (!stcoBox) {\n\t\tthrow new Error('Expected stco box in trak box');\n\t}\n\n\tif (!stscBox) {\n\t\tthrow new Error('Expected stsc box in trak box');\n\t}\n\n\tif (!sttsBox) {\n\t\tthrow new Error('Expected stts box in trak box');\n\t}\n\n\tif (!timescaleAndDuration) {\n\t\tthrow new Error('Expected timescale and duration in trak box');\n\t}\n\n\tconst samplePositions = getSamplePositions({\n\t\tstcoBox,\n\t\tstscBox,\n\t\tstszBox,\n\t\tstssBox,\n\t\tsttsBox,\n\t\tcttsBox,\n\t});\n\n\treturn samplePositions;\n};\n","sourceCodeStart":32,"sourceCodeEnd":64,"githubUrl":"https://github.com/remotion-dev/remotion/blob/78fe4bb3fdb5a2cd68724393d63cb223db333fa7/packages/media-parser/src/containers/iso-base-media/collect-sample-positions-from-trak.ts#L32-L64","documentation":"Thrown by collectSamplePositionsFromTrak when getTimescaleAndDuration(trakBox) returns null. That helper reads the mdhd (Media Header) box inside the trak to obtain timescale and duration; a null result means mdhd is absent or unparseable, so the parser has no time base for the track's samples. mdhd is mandatory in ISO/IEC 14496-12 for every trak.","triggerScenarios":"A trak that is missing its mdia/mdhd child, or whose mdhd is present but parsed as the wrong version/type so getTimescaleAndDuration returns null. Triggered during sample position collection for non-fragmented traks.","commonSituations":"Corrupt files where the moov was partially overwritten. Files from non-conformant encoders that emit trak without mdhd. Truncated moov atoms at EOF. Files surviving an interrupted transfer.","solutions":["Re-mux with `ffmpeg -i input.mp4 -c copy -movflags faststart out.mp4` to rebuild mdhd and the rest of mdia.","Validate the file with `ffprobe -v error -show_streams`; reject if mdhd/timescale is missing.","Catch the error and report the file as unsupported rather than letting it propagate as an unhandled exception.","If authoring files programmatically, ensure every trak includes an mdhd box with a sane timescale."],"exampleFix":"// before\nconst info = await parseMedia({ src: file, fields: { dimensions: true, duration: true } });\n\n// after\nlet info;\ntry {\n  info = await parseMedia({ src: file, fields: { dimensions: true, duration: true } });\n} catch (err) {\n  if (/timescale and duration/i.test(String(err?.message))) {\n    throw new Error('Cannot read track timing: file is corrupt or missing mdhd. Re-mux with ffmpeg.');\n  }\n  throw err;\n}","handlingStrategy":"try-catch","validationCode":"import {execFileSync} from 'node:child_process';\nfunction hasTimescale(file: string): boolean {\n  try {\n    const out = execFileSync('ffprobe', ['-v', 'error', '-show_entries', 'stream=time_base', '-of', 'json', file], {encoding: 'utf8'});\n    const data = JSON.parse(out);\n    return data.streams?.every((s: any) => !!s.time_base && s.time_base !== '0/0');\n  } catch { return false; }\n}","typeGuard":"import type {TrakBox} from './trak/trak';\nimport {getTimescaleAndDuration} from '../../get-fps';\nfunction trakHasTimescale(trakBox: TrakBox): boolean {\n  return getTimescaleAndDuration(trakBox) !== null;\n}","tryCatchPattern":"try {\n  const positions = collectSamplePositionsFromTrak(trakBox);\n} catch (err) {\n  if (/timescale and duration/i.test(String(err?.message))) {\n    return { seekable: false, reason: 'missing-mdhd' };\n  }\n  throw err;\n}","preventionTips":["Reject uploads whose ffprobe time_base is missing or zero.","Always re-mux user content before relying on its sample tables.","Cache the parsed moov so you parse once and validate once, not per-seek.","Treat any null from getTimescaleAndDuration as a hard 'do not use this file' signal."],"tags":["mp4","isobmff","media-parser","corrupt-file","timing","mdhd"],"backgroundTag":null,"analyzedSha":"78fe4bb3fdb5a2cd68724393d63cb223db333fa7","analyzedAt":"2026-08-12T17:18:50.444Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}