{"record":{"id":"cdbc4cee0e4a2744","repo":"remotion-dev/remotion","slug":"videoendtimestamp-must-be-greater-than-or-equal-to","errorCode":null,"errorMessage":"videoEndTimestamp must be greater than or equal to videoStartTimestamp.","messagePattern":"videoEndTimestamp must be greater than or equal to videoStartTimestamp\\.","errorType":"validation","errorClass":"RangeError","httpStatus":null,"severity":"error","filePath":"packages/video-matting/src/prepare-audio.ts","lineNumber":68,"sourceCode":"\tinput: Input;\n\tbaseOutput: Output<WebMOutputFormat, BaseTarget>;\n\tforegroundOutput: Output<WebMOutputFormat, ForegroundTarget>;\n\tdestination: VideoMattingAudioDestination;\n\tvideoStartTimestamp: number;\n\tvideoEndTimestamp: number;\n\taudioQuality: Quality | null;\n\tforceTranscode: boolean;\n}): Promise<PreparedVideoMattingAudio> => {\n\tif (!Number.isFinite(videoStartTimestamp)) {\n\t\tthrow new TypeError('videoStartTimestamp must be a finite number.');\n\t}\n\n\tif (!Number.isFinite(videoEndTimestamp)) {\n\t\tthrow new TypeError('videoEndTimestamp must be a finite number.');\n\t}\n\n\tif (videoEndTimestamp < videoStartTimestamp) {\n\t\tthrow new RangeError(\n\t\t\t'videoEndTimestamp must be greater than or equal to videoStartTimestamp.',\n\t\t);\n\t}\n\n\tif (typeof forceTranscode !== 'boolean') {\n\t\tthrow new TypeError('forceTranscode must be a boolean.');\n\t}\n\n\tif (destination === 'none') {\n\t\treturn {\n\t\t\tprime: () => Promise.resolve(),\n\t\t\twriteAudioUntil: () => Promise.resolve(),\n\t\t\tfinishAudio: () => Promise.resolve(),\n\t\t\tcancel: () => Promise.resolve(),\n\t\t};\n\t}\n\n\tconst audioTrack = await input.getPrimaryAudioTrack();","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/remotion-dev/remotion/blob/b2f4e34732f3c6c222ea029413e22dc402218cd7/packages/video-matting/src/prepare-audio.ts#L50-L86","documentation":"prepareAudio throws a RangeError when videoEndTimestamp is less than videoStartTimestamp, because the audio extraction window would be inverted (negative duration). End must be greater than or equal to start.","triggerScenarios":"Calling prepareAudio where end < start — e.g. swapped arguments, clamping errors, or a segment whose computed start exceeds the video length.","commonSituations":"Off-by-one or unit mistakes (seconds vs milliseconds) making end smaller than start; reversed trim ranges from UI input; sorting bug producing swapped segment boundaries.","solutions":["Ensure videoEndTimestamp >= videoStartTimestamp by validating/clamping before the call","Check for unit mismatches (ms vs s) and swapped argument order","Clamp end to Math.max(start, Math.min(end, duration))"],"exampleFix":"// before\nawait prepareAudio({videoStartTimestamp: end, videoEndTimestamp: start, ...});\n// after\nconst [s, e] = [Math.min(start, end), Math.max(start, end)];\nawait prepareAudio({videoStartTimestamp: s, videoEndTimestamp: e, ...});","handlingStrategy":"validation","validationCode":"if (videoEndTimestamp < videoStartTimestamp) {\n  throw new RangeError('videoEndTimestamp must be >= videoStartTimestamp');\n}\n","typeGuard":"const isValidRange = (s: number, e: number): boolean =>\n  Number.isFinite(s) && Number.isFinite(e) && e >= s;\n","tryCatchPattern":"try {\n  await prepareAudio({videoStartTimestamp: s, videoEndTimestamp: e, ...});\n} catch (err) {\n  if (err instanceof RangeError && err.message.includes('greater than or equal')) {\n    [s, e] = [e, s]; // or clamp: e = s\n  }\n  throw err;\n}\n","preventionTips":["Normalize trim ranges with Math.min/Math.max before calling","Check for ms-vs-s unit errors","Validate UI-provided ranges at input time"],"tags":["range","timestamp","validation"],"backgroundTag":"argument-out-of-range","analyzedSha":"b2f4e34732f3c6c222ea029413e22dc402218cd7","analyzedAt":"2026-09-09T13:27:51.281Z","contentChangedAt":"2026-09-09T13:27:51.281Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}