remotion-dev/remotion · error · Error

Cannot append audio after the time stretcher was finalized.

Error message

Cannot append audio after the time stretcher was finalized.

What it means

The streaming pitch shifter buffers input audio and can only accept data while the time stretcher is still active. Once finalize() has been called the internal buffers are closed for writing, so append() throws this error to prevent corrupting the stretched output. It is a lifecycle/state guard.

Solutions

  1. Ensure all append() calls happen before calling finalize()
  2. Restructure the audio pipeline so finalize() is only called after the last chunk is appended
  3. Create a new StreamingTimeStretcher instance for any additional audio after finalization

Example fix

// before
stretcher.append(lastChunk);
stretcher.finalize();
stretcher.append(lateChunk); // throws
// after
stretcher.append(lastChunk);
stretcher.append(lateChunk);
stretcher.finalize();
Defensive patterns

Strategy: type-guard

Validate before calling

function canAppend(s: {readonly finalized: boolean}): boolean {
  return !s.finalized;
}

Type guard

function isAppendable(s: StreamingTimeStretcher): boolean {
  return !s.finalized;
}

Try / catch

try {
  stretcher.append(audio);
} catch (e) {
  if ((e as Error).message.includes('finalized')) {
    throw new Error('Audio chunk arrived after finalize(); check pipeline ordering', {cause: e});
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling append(planarAudio) on a StreamingTimeStretcher instance after finalize() has already been invoked on the same instance; e.g. pushing late audio chunks in a pipeline that finalized early.

Common situations: Streaming pipelines where the finish/finalize step runs before all audio chunks have been delivered (race condition or wrong ordering in async chunk processing); reusing a finalized stretcher for a second stream.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at packages/media/src/audio/streaming-pitch-shifter.ts:127

		sampleRate: number;
		factor: number;
	}) {
		this.numberOfChannels = numberOfChannels;
		this.factor = factor;
		this.hopSize = Math.max(
			32,
			Math.round((REFERENCE_HOP_SIZE * sampleRate) / REFERENCE_SAMPLE_RATE),
		);
		this.windowSize = this.hopSize * 2;
		this.searchRadius = this.hopSize;
		this.analysisHop = this.hopSize / factor;
		this.input = makePlanarAudio(numberOfChannels, 65_536);
		this.output = makePlanarAudio(numberOfChannels, 65_536);
	}

	public append(audio: PlanarAudio) {
		if (this.finalized) {
			throw new Error(
				'Cannot append audio after the time stretcher was finalized.',
			);
		}

		const {length} = audio[0];
		this.input = ensurePlanarCapacity({
			buffers: this.input,
			requiredLength: this.inputLength + length,
		});

		for (let channel = 0; channel < this.numberOfChannels; channel++) {
			this.input[channel].set(audio[channel], this.inputLength);
		}

		this.inputLength += length;
		this.totalInputFrames += length;
		this.process();
		return this.drainFinalizedOutput();

View on GitHub (pinned to 46a3a6bf13)