remotion-dev/remotion · error · TypeError

"${name}" must be a [number, number] tuple

Error message

"${name}" must be a [number, number] tuple

What it means

Thrown by assertOptionalUvCoordinate() in the linear-progressive-blur effect when `start` or `end` is provided but is not an array of exactly two finite numbers. The field is optional, but once present the library enforces a strict [number, number] shape before any GPU work.

Source

Thrown at packages/effects/src/linear-progressive-blur/index.ts:111

	start: [
		...(params.start ?? DEFAULT_START),
	] as LinearProgressiveBlurUvCoordinate,
	end: [...(params.end ?? DEFAULT_END)] as LinearProgressiveBlurUvCoordinate,
	startBlur: clampBlur(params.startBlur ?? DEFAULT_START_BLUR),
	endBlur: clampBlur(params.endBlur ?? DEFAULT_END_BLUR),
});

const assertOptionalUvCoordinate = (value: unknown, name: string): void => {
	if (value === undefined) {
		return;
	}

	if (
		!Array.isArray(value) ||
		value.length !== 2 ||
		value.some((item) => typeof item !== 'number' || !Number.isFinite(item))
	) {
		throw new TypeError(`"${name}" must be a [number, number] tuple`);
	}
};

const validateLinearProgressiveBlurParams = (
	params: LinearProgressiveBlurParams,
): void => {
	assertEffectParamsObject(params, 'Linear progressive blur');
	assertOptionalUvCoordinate(params.start, 'start');
	assertOptionalUvCoordinate(params.end, 'end');
	assertOptionalFiniteNumber(params.startBlur, 'startBlur');
	assertOptionalFiniteNumber(params.endBlur, 'endBlur');
};

export const linearProgressiveBlur = createEffect<
	LinearProgressiveBlurParams,
	LinearProgressiveBlurState
>({
	type: 'dev.remotion.effects.linearProgressiveBlur',

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pass `start` and `end` as explicit 2-number arrays: linearProgressiveBlur({ start: [0, 0.5], end: [1, 0.5] }).
  2. If the value comes from dynamic data, coerce and validate it into a [number, number] before passing it in.
  3. Keep values finite — avoid NaN/Infinity from divide-by-zero in your own interpolation.
  4. Leave the field undefined to use the defaults ([0,0.5] / [1,0.5]) rather than passing null/empty arrays.

Example fix

// before
linearProgressiveBlur({ start: {x: 0, y: 0.5}, end: [1, 0.5] });
// after
linearProgressiveBlur({ start: [0, 0.5], end: [1, 0.5] });
Defensive patterns

Strategy: type-guard

Validate before calling

import type {LinearProgressiveBlurParams} from '@remotion/effects';

function resolveUvTuple(value: unknown, fallback: readonly [number, number]): readonly [number, number] {
  if (value === undefined) return fallback;
  if (!isUvTuple(value)) throw new TypeError(`expected [number, number], got ${JSON.stringify(value)}`);
  return value;
}

const start = resolveUvTuple(rawInput.start, [0, 0.5]);
const end = resolveUvTuple(rawInput.end, [1, 0.5]);
linearProgressiveBlur({ start, end });

Type guard

const isUvTuple = (v: unknown): v is readonly [number, number] =>
  Array.isArray(v) &&
  v.length === 2 &&
  v.every((n) => typeof n === 'number' && Number.isFinite(n));

Prevention

When it happens

Trigger: Calling linearProgressiveBlur({ start: ... }) or linearProgressiveBlur({ end: ... }) with a value that is not a 2-tuple: e.g. a 3-element array, a single number, a string, NaN/Infinity inside the pair, or an object literal. Thrown synchronously by validateLinearProgressiveBlurParams at effect setup.

Common situations: Passing `{ x, y }` objects instead of tuples; reading coordinates from JSON/config where they deserialize as non-numbers; accidentally spreading a 3-vector; animating the value via interpolate and returning a non-array; copy-paste from a docs example that used a different shape.

Related errors


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