remotion-dev/remotion · error · Error

Video layer output was canceled

Error message

Video layer output was canceled

What it means

Thrown by finalize() in create-video-layer-output when the mediabunny output finished finalizing but a cancellation had been requested in the meantime. The library checks cancellationRequested after each async step of finalization so it never hands back output from an aborted render. It signals that the caller canceled the layer output (or something in the pipeline did) while finalize() was in flight.

Source

Thrown at packages/video-matting/src/create-video-layer-output.ts:220

		})();

		return cancellationPromise;
	};

	const finalize = (): Promise<VideoLayerOutput> => {
		if (cancellationRequested) {
			return Promise.reject(new Error('Video layer output was canceled'));
		}

		if (finalizationPromise !== null) {
			return finalizationPromise;
		}

		finalizationPromise = (async () => {
			try {
				await output.finalize();
				if (cancellationRequested) {
					throw new Error('Video layer output was canceled');
				}

				const mimeType = await output.getMimeType();
				if (cancellationRequested) {
					throw new Error('Video layer output was canceled');
				}

				await closeOutputWritable();
				if (cancellationRequested) {
					throw new Error('Video layer output was canceled');
				}

				finalized = true;

				if (outputMode === 'writable') {
					return {
						dispose: () => Promise.resolve(),
						getBlob: () =>

View on GitHub (pinned to b2f4e34732)

Solutions

  1. Await the cancel() promise and treat finalize()'s rejection as expected — do not retry finalize after cancel
  2. Restructure so cancel() and finalize() are not called concurrently; finalize first, then cancel only on error
  3. Guard with the returned finalizationPromise so only one lifecycle call runs per output
  4. Check output.state before calling finalize; skip finalize if the output was canceled

Example fix

// before
const out = await layerOutput.finalize();
// after
try {
  const out = await layerOutput.finalize();
} catch (e) {
  if (wasCanceled) return; // cancel() was invoked, rejection is expected
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (typeof layerOutput.cancel === 'function') {
  // ensure no cancel() is pending before finalizing
}

Type guard

const canFinalize = (o: {isCanceled?: () => boolean}) => !o.isCanceled?.();

Try / catch

try {
  await layerOutput.finalize();
} catch (e) {
  if ((e as Error).message === 'Video layer output was canceled') return;
  throw e;
}

Prevention

When it happens

Trigger: Calling finalize() on a video layer output while another code path has already invoked cancel() (or discard()) on it; the cancellation lands between output.finalize() and the next check.

Common situations: A user aborts a matting render in the UI mid-encode; a component unmounts and calls cancel() concurrently with finalize(); a timeout wrapper cancels the output while finalize is still awaiting.

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@b2f4e34732 (2026-09-09). Data as JSON: /api/errors/2d08005a40ac1dd9. Report an issue: GitHub.