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 linearGradient when `start` or `end` is provided but is not an array of exactly two finite numbers. These are UV coordinates in [0,1] space defining the gradient endpoints; `undefined` is allowed (optional), but any other non-tuple value is rejected.
Source
Thrown at packages/effects/src/linear-gradient.ts:112
const resolve = (p: LinearGradientParams): LinearGradientResolved => ({
start: [...(p.start ?? DEFAULT_START)] as LinearGradientUvCoordinate,
end: [...(p.end ?? DEFAULT_END)] as LinearGradientUvCoordinate,
startColor: p.startColor ?? DEFAULT_START_COLOR,
endColor: p.endColor ?? DEFAULT_END_COLOR,
});
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 validateLinearGradientParams = (params: LinearGradientParams): void => {
assertEffectParamsObject(params, 'Linear gradient');
assertOptionalUvCoordinate(params.start, 'start');
assertOptionalUvCoordinate(params.end, 'end');
assertOptionalColor(params.startColor, 'startColor');
assertOptionalColor(params.endColor, 'endColor');
};
const VERTEX_SHADER = /* glsl */ `#version 300 es
in vec2 aPos;
in vec2 aUv;
out vec2 vUv;
void main() {
vUv = aUv;View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Pass UV tuples in [0,1]: `start: [0, 0]`, `end: [1, 1]`.
- Convert pixel coords: `start: [px / width, py / height]`.
- Validate both entries are finite numbers before calling.
- Omit `start`/`end` to use the documented defaults.
Example fix
// before
linearGradient({ start: [0, 0], end: [frameWidth, frameHeight] });
// after
linearGradient({ start: [0, 0], end: [1, 1] }); Defensive patterns
Strategy: type-guard
Validate before calling
const isUvTuple = (v: unknown): v is [number, number] =>
Array.isArray(v) &&
v.length === 2 &&
v.every((x) => typeof x === 'number' && Number.isFinite(x));
const start = isUvTuple(userStart) ? userStart : undefined;
linearGradient(start === undefined ? {} : { start }); Type guard
const isUvTuple = (v: unknown): v is [number, number] => Array.isArray(v) && v.length === 2 && v.every((x) => typeof x === 'number' && Number.isFinite(x));
Prevention
- Always pass UV ([0,1]) coordinates, never pixel coordinates.
- Validate the tuple shape before calling; omit the prop to use defaults.
- When deserializing from JSON, coerce both elements to Number and check finiteness.
When it happens
Trigger: Passing `start: [0]`, `start: [0,0,0]`, `start: {x,y}`, `start: ['0','1']`, `start: [0, NaN]`, or `start: [0, Infinity]`. The guard requires a 2-element array where both entries are finite numbers.
Common situations: Passing pixel coordinates instead of UV; passing a Vec2-like object instead of a tuple; numbers deserialized to strings from JSON; off-by-one in array slicing producing wrong length.
Related errors
- "samples" must be >= 1, but got ${samples}
- "${name}" must be a [number, number] tuple
- The "width" and "height" props must be numbers on <Img> when
- The ${formatPropList(conflictingProps)} prop${conflictingPro
- <Solid>: `pixelDensity` must be a positive finite number. Re
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/f65d348db822b9b5.
Report an issue: GitHub.