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-pixelate effect when `start` or `end` is provided but is not an array of exactly two finite numbers. The field is optional, but once present it must match the [number, number] shape before the progressive-pixelate runtime is invoked.

Source

Thrown at packages/effects/src/linear-progressive-pixelate/index.ts:104

	] as LinearProgressivePixelateUvCoordinate,
	end: [
		...(params.end ?? DEFAULT_END),
	] as LinearProgressivePixelateUvCoordinate,
	startBlockSize: params.startBlockSize ?? DEFAULT_START_BLOCK_SIZE,
	endBlockSize: params.endBlockSize ?? DEFAULT_END_BLOCK_SIZE,
});

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 validateBlockSize = (value: number, name: string): void => {
	if (value < 1) {
		throw new TypeError(`"${name}" must be >= 1`);
	}
};

const validateParams = (params: LinearProgressivePixelateParams): void => {
	assertEffectParamsObject(params, 'Linear progressive pixelate');
	assertOptionalUvCoordinate(params.start, 'start');
	assertOptionalUvCoordinate(params.end, 'end');
	assertOptionalFiniteNumber(params.startBlockSize, 'startBlockSize');
	assertOptionalFiniteNumber(params.endBlockSize, 'endBlockSize');
	validateBlockSize(
		params.startBlockSize ?? DEFAULT_START_BLOCK_SIZE,
		'startBlockSize',

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pass `start` and `end` as explicit 2-number arrays: linearProgressivePixelate({ start: [0, 0.5], end: [1, 0.5] }).
  2. Coerce/validate dynamic values into a [number, number] before passing them in.
  3. Keep values finite — guard against NaN/Infinity from your own math.
  4. Omit the field entirely to accept the defaults ([0,0.5] / [1,0.5]) instead of passing null/empty arrays.

Example fix

// before
linearProgressivePixelate({ start: 'left', end: [1, 0.5] });
// after
linearProgressivePixelate({ start: [0, 0.5], end: [1, 0.5] });
Defensive patterns

Strategy: type-guard

Validate before calling

import type {LinearProgressivePixelateParams} 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]);
linearProgressivePixelate({ 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 linearProgressivePixelate({ start: ... }) or linearProgressivePixelate({ end: ... }) with a non-tuple value: a 3-element array, a single number, a string, an `{x,y}` object, or a pair containing NaN/Infinity. Thrown synchronously by validateParams at effect setup.

Common situations: Passing objects instead of tuples; deserializing coordinates from JSON where elements are strings; spreading the wrong array; animating with interpolate and returning a malformed value; reusing a 3-vector from another effect.

Related errors


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