{"record":{"id":"e89fc47e26a5168a","repo":"remotion-dev/remotion","slug":"cannot-append-audio-after-the-time-stretcher-was-finalized","errorCode":null,"errorMessage":"Cannot append audio after the time stretcher was finalized.","messagePattern":"Cannot append audio after the time stretcher was finalized\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/media/src/audio/streaming-pitch-shifter.ts","lineNumber":127,"sourceCode":"\t\tsampleRate: number;\n\t\tfactor: number;\n\t}) {\n\t\tthis.numberOfChannels = numberOfChannels;\n\t\tthis.factor = factor;\n\t\tthis.hopSize = Math.max(\n\t\t\t32,\n\t\t\tMath.round((REFERENCE_HOP_SIZE * sampleRate) / REFERENCE_SAMPLE_RATE),\n\t\t);\n\t\tthis.windowSize = this.hopSize * 2;\n\t\tthis.searchRadius = this.hopSize;\n\t\tthis.analysisHop = this.hopSize / factor;\n\t\tthis.input = makePlanarAudio(numberOfChannels, 65_536);\n\t\tthis.output = makePlanarAudio(numberOfChannels, 65_536);\n\t}\n\n\tpublic append(audio: PlanarAudio) {\n\t\tif (this.finalized) {\n\t\t\tthrow new Error(\n\t\t\t\t'Cannot append audio after the time stretcher was finalized.',\n\t\t\t);\n\t\t}\n\n\t\tconst {length} = audio[0];\n\t\tthis.input = ensurePlanarCapacity({\n\t\t\tbuffers: this.input,\n\t\t\trequiredLength: this.inputLength + length,\n\t\t});\n\n\t\tfor (let channel = 0; channel < this.numberOfChannels; channel++) {\n\t\t\tthis.input[channel].set(audio[channel], this.inputLength);\n\t\t}\n\n\t\tthis.inputLength += length;\n\t\tthis.totalInputFrames += length;\n\t\tthis.process();\n\t\treturn this.drainFinalizedOutput();","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/remotion-dev/remotion/blob/46a3a6bf13ca367376ff18bbb06dae2e87e0d068/packages/media/src/audio/streaming-pitch-shifter.ts#L109-L145","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure all append() calls happen before calling finalize()","Restructure the audio pipeline so finalize() is only called after the last chunk is appended","Create a new StreamingTimeStretcher instance for any additional audio after finalization"],"exampleFix":"// before\nstretcher.append(lastChunk);\nstretcher.finalize();\nstretcher.append(lateChunk); // throws\n// after\nstretcher.append(lastChunk);\nstretcher.append(lateChunk);\nstretcher.finalize();","handlingStrategy":"type-guard","validationCode":"function canAppend(s: {readonly finalized: boolean}): boolean {\n  return !s.finalized;\n}","typeGuard":"function isAppendable(s: StreamingTimeStretcher): boolean {\n  return !s.finalized;\n}","tryCatchPattern":"try {\n  stretcher.append(audio);\n} catch (e) {\n  if ((e as Error).message.includes('finalized')) {\n    throw new Error('Audio chunk arrived after finalize(); check pipeline ordering', {cause: e});\n  }\n  throw e;\n}","preventionTips":["Enforce ordering: append all chunks, then finalize, in a single async sequence (await each append before finalize)","Avoid calling finalize in multiple places (end handler + cleanup); centralize it","If late chunks are possible, buffer them and append before finalizing, or use a new stretcher instance"],"tags":["audio","state","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"46a3a6bf13ca367376ff18bbb06dae2e87e0d068","analyzedAt":"2026-09-18T00:12:20.256Z","contentChangedAt":"2026-09-18T00:12:20.256Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}