remotion-dev/remotion · error · RangeError

writeAudioUntil() timestamps must be monotonically increasin

Error message

writeAudioUntil() timestamps must be monotonically increasing.

What it means

writeAudioUntil() enforces that each successive outputTimestamp is greater than or equal to the previously written timestamp. Media sinks cannot seek backwards while streaming, so a regressing timestamp raises a RangeError to protect the Opus encoder's monotonic timestamp contract.

Source

Thrown at packages/video-matting/src/prepare-audio.ts:242

				if (primed || finished || canceled) {
					return;
				}

				primed = true;
				await writePacketsUntil({
					outputTimestamp: 0,
					writeAtLeastOne: true,
				});
			},
			writeAudioUntil: async (outputTimestamp) => {
				if (!Number.isFinite(outputTimestamp) || outputTimestamp < 0) {
					throw new TypeError(
						'outputTimestamp must be a finite, non-negative number.',
					);
				}

				if (outputTimestamp < lastWriteTimestamp) {
					throw new RangeError(
						'writeAudioUntil() timestamps must be monotonically increasing.',
					);
				}

				if (finished || canceled) {
					return;
				}

				lastWriteTimestamp = outputTimestamp;
				await writePacketsUntil({
					outputTimestamp: Math.min(
						outputTimestamp,
						videoEndTimestamp - videoStartTimestamp,
					),
					writeAtLeastOne: false,
				});
			},
			finishAudio: async () => {

View on GitHub (pinned to b2f4e34732)

Solutions

  1. Ensure each call passes a timestamp >= the last one (monotonic, equal is allowed).
  2. Create a fresh pipeline with prepareAudio() when you need to restart from an earlier timestamp.
  3. Track lastTimestamp in the caller and skip or clamp calls that would go backwards.

Example fix

// before
await audio.writeAudioUntil(currentFrame / fps); // currentFrame can decrease
// after
const t = Math.max(lastT, currentFrame / fps);
await audio.writeAudioUntil(t);
lastT = t;
Defensive patterns

Strategy: validation

Validate before calling

if (t < lastWritten) t = lastWritten; // or skip the call
await audio.writeAudioUntil(t); lastWritten = t;

Type guard

null

Try / catch

try { await audio.writeAudioUntil(t); } catch (e) { if (e instanceof RangeError && e.message.includes('monotonically')) { lastWritten = 0; /* recreate pipeline */ } else throw e; }

Prevention

When it happens

Trigger: Calling writeAudioUntil(5) after writeAudioUntil(10); restarting a render loop without re-calling prepareAudio; replaying a frame sequence with decreasing frame indices.

Common situations: Re-rendering a timeline segment in-place and rewinding the clock; off-by-one in a loop that passes the previous frame's timestamp; reusing a prepared audio pipeline across two renders.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of remotion-dev/remotion@b2f4e34732 (2026-09-09). Data as JSON: /api/errors/fc0246d55e8ae157. Report an issue: GitHub.