remotion-dev/remotion · error · TypeError
"${name}" must be a [number, number] tuple
Error message
"${name}" must be a [number, number] tuple What it means
TypeError thrown by halftone-linear-gradient's assertOptionalUvCoordinate (halftone-linear-gradient.ts:210) when firstStopPosition or secondStopPosition is provided but is not an array of exactly two finite numbers. UV coordinates position the gradient stops in [0..1] texture space.
Source
Thrown at packages/effects/src/halftone-linear-gradient.ts:220
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 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 validateHalftoneLinearGradientParams = (
params: HalftoneLinearGradientParams,
): void => {
assertEffectParamsObject(params, 'Halftone linear gradient');
assertOptionalFiniteNumber(params.firstStopDotSize, 'firstStopDotSize');
assertOptionalFiniteNumber(params.secondStopDotSize, 'secondStopDotSize');
assertOptionalUvCoordinate(params.firstStopPosition, 'firstStopPosition');
assertOptionalUvCoordinate(params.secondStopPosition, 'secondStopPosition');
assertOptionalFiniteNumber(params.gridSize, 'gridSize');
assertOptionalBoolean(params.maskToSourceAlpha, 'maskToSourceAlpha');
assertOptionalEnum(
params.colorMode,
'colorMode',
HALFTONE_LINEAR_GRADIENT_COLOR_MODES,
);View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Provide a two-element numeric tuple, e.g. [0.5, 0.5].
- If converting from an object, map {x,y} -> [x, y] first.
- Filter out NaN/Infinity before passing: tuple.map(Number.isFinite).
- For optional usage, omit the prop entirely rather than passing null/undefined inside the array.
Example fix
// before
<halftoneLinearGradient firstStopPosition={{x: 0, y: 0}} />
// -> "firstStopPosition" must be a [number, number] tuple
// after
<halftoneLinearGradient firstStopPosition={[0, 0]} /> Defensive patterns
Strategy: type-guard
Validate before calling
const toUv = (v: unknown): [number, number] => {
if (Array.isArray(v) && v.length === 2 && v.every(n => typeof n === 'number' && Number.isFinite(n))) {
return [v[0], v[1]];
}
return [0, 0]; // or throw
}; Type guard
const isUvCoordinate = (v: unknown): v is [number, number] => Array.isArray(v) && v.length === 2 && v.every((item) => typeof item === 'number' && Number.isFinite(item));
Try / catch
null
Prevention
- Pass UV stops as plain tuples, not objects.
- Map {x,y} -> [x, y] before passing.
- Filter NaN/Infinity out of computed tuples.
- To leave a stop default, omit the prop entirely.
When it happens
Trigger: validateHalftoneLinearGradientParams calls assertOptionalUvCoordinate for firstStopPosition (line 230) and secondStopPosition (line 231). Triggered by: a non-array (object/string/number); an array of length other than 2; an array containing a non-number, NaN, or Infinity.
Common situations: Passing [0.5] or [0.1, 0.2, 0.3]; passing {x:0.5, y:0.5} instead of a tuple; a NaN leaking from a division; a string like '0.5,0.5'.
Related errors
- "${name}" must be ${formatEnum(variants)}
- "${name}" must be >= 0, but got ${JSON.stringify(value)}
- "${name}" must be greater than 0, but got ${JSON.stringify(v
- "dotColor" can only be set when "colorMode" is "solid"
- "${name}" must be a [number, number] tuple
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/e7c905b5b1311a30.
Report an issue: GitHub.