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 radialProgressiveBlur is given a `center` value that is not undefined and not a 2-element array of finite numbers. The center is a UV coordinate pair, so the runtime strictly requires the [number, number] tuple shape before any GL work happens. A TypeError (not Error) is used because this is a caller-contract violation.
Source
Thrown at packages/effects/src/radial-progressive-blur/index.ts:163
width: params.width ?? DEFAULT_WIDTH,
height: params.height ?? DEFAULT_HEIGHT,
rotation: params.rotation ?? DEFAULT_ROTATION,
start: params.start ?? DEFAULT_START,
startBlur: clampBlur(params.startBlur ?? DEFAULT_START_BLUR),
endBlur: clampBlur(params.endBlur ?? DEFAULT_END_BLUR),
});
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 validateRadialProgressiveBlurParams = (
params: RadialProgressiveBlurParams,
): void => {
assertEffectParamsObject(params, 'Radial progressive blur');
assertOptionalUvCoordinate(params.center, 'center');
assertOptionalFiniteNumber(params.width, 'width');
assertOptionalFiniteNumber(params.height, 'height');
assertOptionalFiniteNumber(params.rotation, 'rotation');
validateNonNegative(params.width ?? DEFAULT_WIDTH, 'width');
validateNonNegative(params.height ?? DEFAULT_HEIGHT, 'height');
assertOptionalFiniteNumber(params.start, 'start');
validateUnitInterval(params.start ?? DEFAULT_START, 'start');
assertOptionalFiniteNumber(params.startBlur, 'startBlur');
assertOptionalFiniteNumber(params.endBlur, 'endBlur');
};View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Pass exactly two finite numbers: `radialProgressiveBlur({center: [0.5, 0.5]})`.
- When animating, build the tuple with an explicit expression: `center: [interpolate(...), interpolate(...)] as [number, number]`.
- If you do not need to set center, omit it entirely — it defaults to a valid value.
- Convert library vector objects to plain arrays before passing: `[vec.x, vec.y]`.
- Validate deserialized input with the type guard below before feeding it to the effect.
Example fix
// before
import {radialProgressiveBlur} from '@remotion/effects';
const e = radialProgressiveBlur({center: {x: 0.5, y: 0.5}}); // throws TypeError
// or
const e2 = radialProgressiveBlur({center: [0.5]}); // throws TypeError
// after
const e = radialProgressiveBlur({center: [0.5, 0.5]});
// or omit it:
const e3 = radialProgressiveBlur({}); Defensive patterns
Strategy: type-guard
Validate before calling
function asUvCoordinate(v: unknown): [number, number] | undefined {
if (v === undefined) return undefined;
if (
Array.isArray(v) &&
v.length === 2 &&
v.every((n) => typeof n === 'number' && Number.isFinite(n))
) {
return [v[0], v[1]];
}
throw new TypeError('"center" must be a [number, number] tuple');
}
const center = asUvCoordinate(maybeCenter);
const effect = radialProgressiveBlur(center === undefined ? {} : {center}); Type guard
function isUvCoordinate(v: unknown): v is [number, number] {
return (
Array.isArray(v) &&
v.length === 2 &&
v.every((n) => typeof n === 'number' && Number.isFinite(n))
);
} Prevention
- Always pass `center` as a plain 2-element number array literal.
- When animating, build the tuple explicitly: `[x, y] as [number, number]`.
- Convert library vector objects to plain arrays at the boundary.
- Use the isUvCoordinate guard on deserialized input before feeding it to the effect.
- If you do not need to override center, omit it entirely.
When it happens
Trigger: Calling `radialProgressiveBlur({center: ...})` with a value that is: an array of length ≠ 2 (e.g. `[0.5]` or `[0.5, 0.5, 0.5]`); an array containing a non-number or non-finite element (e.g. `[0.5, NaN]`, `[0.5, '0.5']`, `[0.5, null]`); or a non-array truthy object (e.g. `{x: 0.5, y: 0.5}`).
Common situations: Passing a Vec2/Vector2 object from another library instead of a plain array; animating center with interpolate() but returning 3 or 1 components; copy-pasting a value from an effect that uses [x, y, z]; deserializing JSON where the tuple lost a component; passing `[0.5, undefined]`.
Related errors
- "${name}" must be a [number, number] tuple
- "${name}" must be greater than 0, but got ${JSON.stringify(v
- "${name}" must be greater than or equal to 0, but got ${JSON
- "colors" must be an array with at least 2 colors, but got ${
- "exposure" must be >= ${MIN_EXPOSURE}, but got ${JSON.string
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/77d69031cecf258a.
Report an issue: GitHub.