remotion-dev/remotion · error
Expected LUT colors, got .
Error message
Expected ${data.length / 3} LUT colors, got ${offset / 3}. What it means
The number of color rows in a .cube file must exactly match the declared size: size rows for 1D LUTs, size^3 for 3D LUTs. After parsing, if the number of collected colors (offset/3) differs from data.length/3, this error is thrown reporting both counts. Unlike the 'got more' variant this fires when the table is incomplete — too few color lines.
Solutions
- Re-export or re-download the LUT file — truncation is the usual cause.
- Adjust the size header down to match the actual row count if the table itself is intact (count data lines: 1D -> n, 3D -> cube root of n).
- Compare the reported 'Expected X LUT colors, got Y' counts to determine whether the header or the data is wrong.
- Validate the file length beforehand: count non-header lines and check against size^3.
Example fix
// before: 32768 rows but header says 33^3=35937 LUT_3D_SIZE 33 // after (table has 32^3=32768 rows) LUT_3D_SIZE 32
Defensive patterns
Strategy: validation
Validate before calling
const size = Number(/LUT_3D_SIZE\s+(\d+)/.exec(text)?.[1] ?? NaN);
const rows = text.split(/\r?\n/).filter((l) => {
const p = l.trim().split(/\s+/);
return p.length === 3 && p.every((x) => Number.isFinite(Number(x)));
}).length;
if (Number.isFinite(size) && rows !== size ** 3) {
throw new Error(`Expected ${size ** 3} colors, file has ${rows}`);
} Try / catch
try {
const lut = parseCubeLut(text);
} catch (e) {
const m = /Expected (\d+) LUT colors, got (\d+)/.exec((e as Error).message);
if (m) console.error(`Table truncated: have ${m[2]} of ${m[1]} colors`);
} Prevention
- Verify downloads complete (compare file size/checksum) before parsing
- Re-export truncated LUTs from the source tool
- Match the size header to the actual cube root of the data row count
When it happens
Trigger: parseCubeLut(text) where declared LUT_3D_SIZE is 33 but only e.g. 30000 color lines follow (truncated download), or a 1D LUT declaring 32 entries but containing fewer; also a wrong size value (declaring 33 for a 32-entry table).
Common situations: Interrupted downloads/uploads truncating the tail of the file; text editors or copy-paste dropping the last lines; size header incremented without regenerating the table.
Understand the failure class
Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.
Related errors
- Combined 1D and 3D LUTs are not supported.
- Duplicate on line .
- Expected LUT colors, got more.
- Expected LUT_1D_SIZE or LUT_3D_SIZE.
- Expected LUT_1D_SIZE or LUT_3D_SIZE before color data.
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/bdcfded6c093c8c6.
Report an issue: GitHub.
Appendix: source
Thrown at packages/studio/src/helpers/cube-lut.ts:130
!color.every((value) => Number.isFinite(Math.fround(value)))
) {
throw new Error(`Invalid LUT color on line ${index + 1}.`);
}
if (offset + 3 > data.length) {
throw new Error(`Expected ${data.length / 3} LUT colors, got more.`);
}
data.set(color, offset);
offset += 3;
}
if (type === null || data === null) {
throw new Error('Expected LUT_1D_SIZE or LUT_3D_SIZE.');
}
if (offset !== data.length) {
throw new Error(
`Expected ${data.length / 3} LUT colors, got ${offset / 3}.`,
);
}
if (domainMin.some((min, channel) => min >= domainMax[channel])) {
throw new Error(
'DOMAIN_MAX must be greater than DOMAIN_MIN in every channel.',
);
}
if (
domainMin.some((min, channel) => !Number.isFinite(domainMax[channel] - min))
) {
throw new Error('LUT input ranges must be finite.');
}
if (
(headers.has('LUT_1D_INPUT_RANGE') || headers.has('LUT_3D_INPUT_RANGE')) &&View on GitHub (pinned to 46a3a6bf13)