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 linearGradientTint 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 (the param is optional), but any other non-tuple value is rejected.
Source
Thrown at packages/effects/src/linear-gradient-tint.ts:136
const resolve = (p: LinearGradientTintParams): LinearGradientTintResolved => ({
start: [...(p.start ?? DEFAULT_START)] as LinearGradientTintUvCoordinate,
end: [...(p.end ?? DEFAULT_END)] as LinearGradientTintUvCoordinate,
startColor: p.startColor ?? DEFAULT_START_COLOR,
endColor: p.endColor ?? DEFAULT_END_COLOR,
amount: p.amount ?? DEFAULT_AMOUNT,
});
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 validateLinearGradientTintParams = (
params: LinearGradientTintParams,
): void => {
assertEffectParamsObject(params, 'Linear gradient tint');
assertOptionalUvCoordinate(params.start, 'start');
assertOptionalUvCoordinate(params.end, 'end');
assertOptionalColor(params.startColor, 'startColor');
assertOptionalColor(params.endColor, 'endColor');
assertOptionalFiniteNumber(params.amount, 'amount');
validateUnitInterval(resolve(params).amount, 'amount');
};
const VERTEX_SHADER = /* glsl */ `#version 300 es
in vec2 aPos;
in vec2 aUv;View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Pass UV (0..1) tuples: `start: [0, 0.5]`, `end: [1, 0.5]`.
- Convert pixel coords: `start: [px / width, py / height]`.
- Validate before calling: ensure both elements are numbers and finite.
- Omit `start`/`end` to accept the documented defaults.
Example fix
// before
linearGradientTint({ start: [x, y], end: [w, h] }); // pixel space
// after
linearGradientTint({ start: [x / w, y / h], end: [1, 0.5] }); 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;
linearGradientTint(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.5]` (length 1), `start: [0, 0, 0]` (length 3), `start: {x:0,y:0}`, `start: ['0','1']` (strings), `start: [0, NaN]`, or `start: [0, Infinity]`. The guard requires a 2-element array where both entries are finite numbers.
Common situations: Mixing up pixel coordinates and UV coordinates; passing a Vec2-like object instead of a tuple; deserializing from JSON where numbers became strings; using `[width/2, height/2]` (pixel-space) instead of `[0.5, 0.5]`.
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/70dcc9d896279017.
Report an issue: GitHub.