remotion-dev/remotion · error · TypeError

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

Error message

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

What it means

pattern() validates its optional 'origin' prop (a UV coordinate) and requires it, when supplied, to be an array of exactly two finite numbers. This TypeError fires before any GPU work when the value is present but malformed (wrong length, non-array, or non-finite entries like NaN/Infinity).

Source

Thrown at packages/effects/src/pattern.ts:231

	rowOffset: p.rowOffset ?? DEFAULT_OFFSET,
	rowOffsetEvery: p.rowOffsetEvery ?? DEFAULT_OFFSET_EVERY,
	columnOffset: p.columnOffset ?? DEFAULT_OFFSET,
	columnOffsetEvery: p.columnOffsetEvery ?? DEFAULT_OFFSET_EVERY,
	origin: [...(p.origin ?? DEFAULT_ORIGIN)] as PatternOrigin,
	wrap: p.wrap ?? DEFAULT_WRAP,
});

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 assertOptionalInteger = (value: unknown, name: string): void => {
	if (value === undefined) {
		return;
	}

	if (!Number.isInteger(value)) {
		throw new TypeError(
			`"${name}" must be an integer, but got ${JSON.stringify(value)}`,
		);
	}
};

const validatePositive = (value: number, name: string): void => {
	if (value <= 0) {
		throw new TypeError(`"${name}" must be > 0`);

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pass origin as a readonly [number, number] tuple of finite values, e.g. [0.5, 0.5].
  2. Omit origin entirely to accept the default [0, 0].
  3. Coerce inputs from your data layer into the tuple shape before calling pattern().
  4. If animating, guard the computed components with Number.isFinite before forwarding.

Example fix

// before
pattern({ origin: 'center' })
pattern({ origin: [0.5] })

// after
pattern({ origin: [0.5, 0.5] })
// or omit it:
pattern({ scale: 0.2 })
Defensive patterns

Strategy: type-guard

Validate before calling

// Validate origin before calling pattern().
function isValidOrigin(v: unknown): v is readonly [number, number] {
  return Array.isArray(v) && v.length === 2 && v.every((n) => typeof n === 'number' && Number.isFinite(n));
}
if (originInput !== undefined && !isValidOrigin(originInput)) {
  throw new Error('origin must be a [number, number] tuple of finite values');
}
pattern({ origin: originInput });

Type guard

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

Prevention

When it happens

Trigger: Calling pattern({ origin: <value> }) where <value> is defined but not a 2-tuple of finite numbers, e.g. origin: [0.5], origin: 'center', origin: [0, NaN], origin: {x:0,y:0}. The check assertOptionalUvCoordinate runs in validatePatternParams during effect setup.

Common situations: Passing origin as a string or object from a config/UI; reading origin from JSON that truncated the array; animated values producing NaN via division by zero; passing a 3-element vector by mistake.

Related errors


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