remotion-dev/remotion · error
The time stretcher has already been finalized.
Error message
The time stretcher has already been finalized.
What it means
finalize() on StreamingPitchShifter flushes remaining audio and marks the instance finished. Calling finalize twice would produce duplicated tail padding/invalid state, so the second call throws this error.
Source
Thrown at packages/media/src/audio/pitch-shift.ts:323
this.outputLength -= finalizedLength;
this.synthesisPosition -= finalizedLength;
const inputFramesToDiscard = Math.max(
0,
Math.floor(this.analysisPosition) - this.searchRadius,
);
for (let channel = 0; channel < this.numberOfChannels; channel++) {
this.input[channel].copyWithin(0, inputFramesToDiscard, this.inputLength);
}
this.inputLength -= inputFramesToDiscard;
this.analysisPosition -= inputFramesToDiscard;
return result;
}
public finalize() {
if (this.finalized) {
throw new Error('The time stretcher has already been finalized.');
}
this.finalized = true;
const targetLength = Math.round(this.totalInputFrames * this.factor);
const padding = makePlanarAudio(
this.numberOfChannels,
this.windowSize + this.searchRadius * 2,
);
this.input = ensurePlanarCapacity({
buffers: this.input,
requiredLength: this.inputLength + padding[0].length,
});
for (let channel = 0; channel < this.numberOfChannels; channel++) {
this.input[channel].set(padding[channel], this.inputLength);
}
this.inputLength += padding[0].length;
this.process();View on GitHub (pinned to a6a7485a9a)
Solutions
- Call finalize exactly once per StreamingPitchShifter instance; guard with a boolean flag in caller code
- Remove duplicate flush calls (e.g. from both catch/finally and normal completion)
- On retry, create a new shifter instead of re-finalizing the old one
Example fix
// before shifter.finalize(); cleanup(); // cleanup also calls shifter.finalize() // after shifter.finalize(); cleanup(); // cleanup: if (!finalized) shifter.finalize();
Defensive patterns
Strategy: validation
Validate before calling
let finalized = false;
function finalizeOnce(s: StreamingPitchShifter) {
if (!finalized) { s.finalize(); finalized = true; }
} Type guard
const canFinalize = (s: StreamingPitchShifter): boolean => !s.finalized;
Try / catch
try {
shifter.finalize();
} catch (e) {
if (e instanceof Error && e.message.includes('already been finalized')) {
// already flushed — safe to ignore
} else throw e;
} Prevention
- Finalize exactly once per instance; centralize the call in one code path
- Avoid finalize in both catch and finally blocks
- Use a boolean flag or idempotent wrapper around finalize
- For retries, build a fresh shifter
When it happens
Trigger: Calling shifter.finalize() more than once on the same instance — e.g. flush() invoked on both segment end and stream end, or finalize in both an error-cleanup path and the normal completion path.
Common situations: Double flush in streaming pipelines (error handler + finally block); reusing an iterator's internal shifter after segment boundary flush; retry logic calling finalize again after a partial failure.
Related errors
- Cannot append audio after the time stretcher was finalized.
- Pitch shifter was not initialized.
- No audio stream found in '${input_path}'. Ensure the video c
- Could not determine duration of ${src}, but "loop" was set.
- No audio track found
AI-assisted analysis of remotion-dev/remotion@a6a7485a9a (2026-09-02).
Data as JSON: /api/errors/d7e35b3b392a4a30.
Report an issue: GitHub.