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 when the fisheye() effect receives a 'center' value that is defined but is not an array of exactly two finite numbers. The fisheye effect expects 'center' as a UV-space [x, y] coordinate (each 0..1) so the lens distortion can be offset within the frame. Any other shape (string, object, length-3 array, NaN/Infinity member, nested arrays) is rejected because the GLSL sampler would otherwise read garbage.

Source

Thrown at packages/effects/src/fisheye/index.ts:110

const resolve = (p: FisheyeParams): FisheyeResolved => ({
	fieldOfView: p.fieldOfView ?? DEFAULT_FISHEYE_FIELD_OF_VIEW,
	center: [...(p.center ?? DEFAULT_FISHEYE_CENTER)] as FisheyeUvCoordinate,
	radius: p.radius ?? DEFAULT_FISHEYE_RADIUS,
	zoom: p.zoom ?? DEFAULT_FISHEYE_ZOOM,
});

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 validatePositive = (value: number, name: string): void => {
	if (value <= 0) {
		throw new TypeError(
			`"${name}" must be greater than 0, but got ${JSON.stringify(value)}`,
		);
	}
};

const validateFisheyeParams = (params: FisheyeParams): void => {
	assertEffectParamsObject(params, 'Fisheye');
	assertOptionalFiniteNumber(params.fieldOfView, 'fieldOfView');
	assertOptionalUvCoordinate(params.center, 'center');
	assertOptionalFiniteNumber(params.radius, 'radius');
	assertOptionalFiniteNumber(params.zoom, 'zoom');

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pass center as an explicit two-number tuple, e.g. fisheye({center: [0.5, 0.5]}).
  2. If the value comes from JSON or user input, coerce and validate with Number() and isFinite before calling fisheye().
  3. Leave center undefined to use the default [0.5, 0.5].

Example fix

// before
fisheye({center: 0.5});
fisheye({center: ['0.5', '0.5']});
// after
fisheye({center: [0.5, 0.5]});
fisheye({center: [Number(raw.x), Number(raw.y)]});
Defensive patterns

Strategy: type-guard

Validate before calling

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

if (center !== undefined && !isUvTuple(center)) {
  throw new Error('center must be a [number, number] tuple');
}

Type guard

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

Prevention

When it happens

Trigger: Calling fisheye({center: 0.5}) with a scalar, fisheye({center: [0.5]}) or fisheye({center: [0.5, 0.5, 0.5]}) with the wrong arity, fisheye({center: ['0.5', '0.5']}) with strings, fisheye({center: [0.5, NaN]}), or passing an object like {x,y} instead of a tuple.

Common situations: Porting center coordinates from libraries that accept {x, y} objects (Framer Motion, GSAP) or [x, y, z] triples; deserializing center from JSON where numbers became strings; off-by-one copy of a vec3 from a 3D scene.

Related errors


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