remotion-dev/remotion · error

The mediaParserController() was used in multiple parseMedia(

Error message

The mediaParserController() was used in multiple parseMedia() calls. Create a separate controller for each call.

What it means

Thrown inside attachSeekingHintResolution when parseMedia tries to register its seeking-hints resolver on a controller that already has one. Because each controller is single-use, a second registration means the same controller was handed to a second parseMedia() call. The guard prevents the two parses from racing on one resolver.

Source

Thrown at packages/media-parser/src/controller/media-parser-controller.ts:89

		return seekingHintResolution();
	};

	const simulateSeek = (seekInSeconds: number) => {
		if (!simulateSeekResolution) {
			throw new Error(
				'The mediaParserController() was not yet used in a parseMedia() call',
			);
		}

		return simulateSeekResolution(seekInSeconds);
	};

	const attachSeekingHintResolution = (
		callback: () => Promise<SeekingHints | null>,
	) => {
		if (seekingHintResolution) {
			throw new Error(
				'The mediaParserController() was used in multiple parseMedia() calls. Create a separate controller for each call.',
			);
		}

		seekingHintResolution = callback;
	};

	const attachSimulateSeekResolution = (
		callback: (seekInSeconds: number) => Promise<SeekResolution>,
	) => {
		if (simulateSeekResolution) {
			throw new Error(
				'The mediaParserController() was used in multiple parseMedia() calls. Create a separate controller for each call.',
			);
		}

		simulateSeekResolution = callback;
	};

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Create a fresh mediaParserController() for every parseMedia() invocation; store it in component/request scope, not module scope.
  2. If you need coordinated abort across multiple parses, share an AbortSignal via a single parent controller and create separate child controllers for each parseMedia call.
  3. Abort and discard the old controller before starting a new parse: controller.abort() then make a new one.

Example fix

// before
const controller = mediaParserController();
await parseMedia({src: a, controller});
await parseMedia({src: b, controller}); // throws

// after
const ctrlA = mediaParserController();
await parseMedia({src: a, controller: ctrlA});
const ctrlB = mediaParserController();
await parseMedia({src: b, controller: ctrlB});
Defensive patterns

Strategy: validation

Validate before calling

const makeRun = (src) => {
  const controller = mediaParserController();
  parseMedia({src, controller, fields: {duration: true}});
  return controller;
};
const c1 = makeRun(a); // separate controller per run
const c2 = makeRun(b);

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Passing the same controller instance to a second parseMedia() call while (or after) the first is still using it. Running parseMedia in a loop with a controller declared outside the loop.

Common situations: Building a player that re-parses when the source changes but reuses the controller variable. Concurrent parseMedia calls (e.g. video + separate audio) sharing one controller for shared abort/seek control. Hot-reload or React strict-mode double-invoke reusing a controller from module scope.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/54159385b3f3d6c2. Report an issue: GitHub.