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 the corner-pin effect's assertOptionalUvCoordinate guard when one of the corner props (topLeft, topRight, bottomRight, bottomLeft) is supplied but is not a two-element array of finite numbers. The corner-pin effect maps the four output corners via UV coordinates, so each corner must be a strict [number, number] tuple. Passing objects, shorter/longer arrays, NaN/Infinity, or non-number elements trips this guard before any WebGL work happens.

Source

Thrown at packages/effects/src/corner-pin/index.ts:88

	bottomRight: [
		...(p.bottomRight ?? DEFAULT_BOTTOM_RIGHT),
	] as CornerPinUvCoordinate,
	bottomLeft: [
		...(p.bottomLeft ?? DEFAULT_BOTTOM_LEFT),
	] as CornerPinUvCoordinate,
});

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 validateCornerPinParams = (params: CornerPinParams): void => {
	assertEffectParamsObject(params, 'Corner pin');
	assertOptionalUvCoordinate(params.topLeft, 'topLeft');
	assertOptionalUvCoordinate(params.topRight, 'topRight');
	assertOptionalUvCoordinate(params.bottomRight, 'bottomRight');
	assertOptionalUvCoordinate(params.bottomLeft, 'bottomLeft');
};

export const cornerPin = createEffect<CornerPinParams, CornerPinState>({
	type: 'dev.remotion.effects.cornerPin',
	label: 'cornerPin()',
	documentationLink: 'https://www.remotion.dev/docs/effects/corner-pin',
	backend: 'webgl2',
	calculateKey: (params) => {
		const r = resolve(params);

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pass each corner as a literal two-element numeric array, e.g. topLeft: [0.25, 0.1].
  2. If coords arrive as objects, map them: topLeft: [pt.x, pt.y] before passing to cornerPin().
  3. Guard against NaN/Infinity upstream: filter coordinates through Number.isFinite before constructing the params object.
  4. Omit the corner entirely to accept the documented default rather than passing null or undefined-like sentinels.

Example fix

// before
cornerPin({topLeft: {x: 0, y: 0}, bottomRight: [1, 1]});

// after
const toUv = (p) => [p.x, p.y];
cornerPin({topLeft: toUv({x: 0, y: 0}), bottomRight: [1, 1]});
Defensive patterns

Strategy: type-guard

Validate before calling

const isUv = (v) =>
  Array.isArray(v) &&
  v.length === 2 &&
  v.every((n) => typeof n === 'number' && Number.isFinite(n));

const safeParams = (p) => {
  for (const k of ['topLeft', 'topRight', 'bottomRight', 'bottomLeft']) {
    if (p[k] !== undefined && !isUv(p[k])) {
      throw new Error(`${k} must be [number, number]`);
    }
  }
  return p;
};

Type guard

import type {CornerPinParams, CornerPinUvCoordinate} from '@remotion/effects';

const isUvCoordinate = (v: unknown): v is CornerPinUvCoordinate =>
  Array.isArray(v) &&
  v.length === 2 &&
  v.every((n) => typeof n === 'number' && Number.isFinite(n));

const isCornerPinParams = (p: unknown): p is CornerPinParams => {
  if (typeof p !== 'object' || p === null) return false;
  const rec = p as Record<string, unknown>;
  for (const k of ['topLeft', 'topRight', 'bottomRight', 'bottomLeft']) {
    if (rec[k] !== undefined && !isUvCoordinate(rec[k])) return false;
  }
  return true;
};

Prevention

When it happens

Trigger: Calling cornerPin() with topLeft: {x: 0, y: 0} (object instead of array), topLeft: [0] or topLeft: [0, 0, 0] (wrong arity), topLeft: [0, '1'] (string element), topLeft: [NaN, 0] or topLeft: [Infinity, 0] (non-finite), or topLeft: null (null is not undefined and fails Array.isArray). The check runs inside validateCornerPinParams, which the effect invokes before setup.

Common situations: Authoring a corner-pin animation from an external data source whose coordinates come back as objects; serializing tuples through JSON that mutated into objects; passing the output of a math library that returns BigNumbers or strings; copy-pasting a 3-tuple from a 3D tool; animating values through a Spring whose interpolation momentarily yields NaN.

Related errors


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