remotion-dev/remotion · error · TypeError

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

Error message

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

What it means

TypeError thrown by vignette's assertOptionalUvCoordinate when params.center is defined but is not exactly a 2-element array of finite numbers. The validator requires [number, number]; it rejects arrays of other lengths, non-arrays, and elements that are not finite numbers (NaN/Infinity included). center is optional, so undefined is accepted and defaults to [0.5, 0.5].

Source

Thrown at packages/effects/src/vignette.ts:184

	radius: p.radius ?? DEFAULT_RADIUS,
	feather: p.feather ?? DEFAULT_FEATHER,
	roundness: p.roundness ?? DEFAULT_ROUNDNESS,
	color: p.color ?? DEFAULT_COLOR,
	mode: p.mode ?? DEFAULT_MODE,
	center: [...(p.center ?? DEFAULT_CENTER)] as VignetteCenter,
});

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 validateVignetteParams = (params: VignetteParams): void => {
	assertEffectParamsObject(params, 'Vignette');
	assertOptionalFiniteNumber(params.amount, 'amount');
	assertOptionalFiniteNumber(params.radius, 'radius');
	assertOptionalFiniteNumber(params.feather, 'feather');
	assertOptionalFiniteNumber(params.roundness, 'roundness');
	assertOptionalColor(params.color, 'color');
	assertOptionalEnum(params.mode, 'mode', VIGNETTE_MODES);
	assertOptionalUvCoordinate(params.center, 'center');

	const r = resolve(params);
	validateUnitInterval(r.amount, 'amount');
	validateUnitInterval(r.radius, 'radius');
	validateUnitInterval(r.feather, 'feather');
	validateUnitInterval(r.roundness, 'roundness');

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pass exactly a [number, number] tuple of finite values, e.g. center: [0.5, 0.5].
  2. If center is optional for your case, omit it to use the default.
  3. Sanitise dynamic input: coerce with Number() and guard Number.isFinite before building the tuple.
  4. Type the prop as readonly [number, number] so the compiler flags shape mistakes.

Example fix

// before
const e = vignette({center: {x: 0.5, y: 0.5}}); // throws TypeError

// after
const e = vignette({center: [0.5, 0.5]});
// or omit
const e2 = vignette({});
Defensive patterns

Strategy: type-guard

Validate before calling

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

const center = isFiniteTuple2(config.center) ? config.center : undefined;
const e = vignette({center, amount: 0.6});

Type guard

const isUvCoordinate = (v: unknown): v is [number, number] =>
  Array.isArray(v) &&
  v.length === 2 &&
  typeof v[0] === 'number' &&
  typeof v[1] === 'number' &&
  Number.isFinite(v[0]) &&
  Number.isFinite(v[1]);

Try / catch

try {
  const e = vignette({center: config.center, amount: 0.6});
} catch (err) {
  if (err instanceof TypeError) {
    console.error('Invalid vignette center:', config.center, err);
  }
  throw err;
}

Prevention

When it happens

Trigger: vignette({center: [0.5]}) (length 1), vignette({center: [0.5, 0.5, 0.5]}) (length 3), vignette({center: 'center'}), vignette({center: {x: 0.5, y: 0.5}}), vignette({center: [0.5, NaN]}), or vignette({center: [0.5, '0.5']}).

Common situations: Passing an {x,y} object instead of a tuple; truncating or extending the array; serialised values where a number became a string; feeding mouse/pointer coordinates that yield NaN.

Related errors


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