remotion-dev/remotion · error · TypeError
Invalid LUT content: expected
Error message
Invalid LUT content: expected ${expectedRows} color rows for LUT_3D_SIZE ${size}, but got ${dataRows} What it means
parseCubeLut() counts non-header color data rows and requires exactly size^3 rows for the declared LUT_3D_SIZE. If the number of data rows differs (too few or too many), the file is inconsistent with its declared size and a TypeError is thrown naming expected vs actual row counts.
Solutions
- Compare the reported got/expected counts: fix LUT_3D_SIZE to match the actual number of color rows, or regenerate the LUT with the correct size
- Re-download/re-export the LUT — truncation is the most common cause when got < expected
- Remove trailing junk lines or duplicated rows so exactly size^3 data rows remain
- Check the file size math: each row is one line of 3 numbers; a 33-size LUT must have 35937 data lines
Example fix
// before (.cube header mismatch) LUT_3D_SIZE 33 // ...only 4913 data rows (a 17-size cube) // after LUT_3D_SIZE 17 // (or regenerate the table with 35937 rows for size 33)
Defensive patterns
Strategy: validation
Validate before calling
const lines = content.split(/\r?\n/)
.map((l) => l.split('#')[0].trim())
.filter((l) => l.length > 0);
const sizeLine = lines.find((l) => /^LUT_3D_SIZE\s+\d+$/.test(l));
const size = sizeLine ? Number(sizeLine.split(/\s+/)[1]) : null;
const isData = (l: string) => /^[\d.eE+-]+\s+[\d.eE+-]+\s+[\d.eE+-]+$/.test(l);
if (size !== null) {
const dataRows = lines.filter(isData).length;
if (dataRows !== size ** 3) throw new Error(`Expected ${size ** 3} rows, found ${dataRows}`);
} Try / catch
try {
lut = parseCubeLut(content);
} catch (e) {
if (e instanceof TypeError && /color rows for LUT_3D_SIZE/.test(e.message)) {
// re-download or regenerate the LUT; message states expected vs got
}
throw e;
} Prevention
- Re-download truncated LUT files instead of parsing them anyway
- Never edit LUT_3D_SIZE without regenerating the data table
- Sanity-check the file's line count against size^3 before parsing
When it happens
Trigger: Calling parseCubeLut(content) on a truncated .cube file (download cut off), a file where LUT_3D_SIZE was edited without regenerating the data table (e.g. says 33 but contains 17^3=4913 rows), extra garbage lines parsed as data rows, or rows removed/added by hand-editing.
Common situations: Incomplete uploads/downloads, converting a smaller LUT by only editing the size header, files that mix CRLF/LF or contain stray tokens miscounted as data, and LUTs from tools writing fewer rows than declared.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid LUT content: DOMAIN_MIN must be smaller than…
- Invalid LUT content: LUT_3D_SIZE is missing
- Invalid LUT content on line
- Combined 1D and 3D LUTs are not supported.
- Conflicting LUT input range headers.
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/03fab41ad3b6d8f4.
Report an issue: GitHub.
Appendix: source
Thrown at packages/effects/src/lut/parse-cube-lut.ts:200
);
}
}
if (size === null) {
throw new TypeError('Invalid LUT content: LUT_3D_SIZE is missing');
}
for (let channel = 0; channel < 3; channel++) {
if (domainMin[channel] >= domainMax[channel]) {
throw new TypeError(
`Invalid LUT content: DOMAIN_MIN must be smaller than DOMAIN_MAX for every channel`,
);
}
}
const expectedRows = size ** 3;
if (dataRows !== expectedRows) {
throw new TypeError(
`Invalid LUT content: expected ${expectedRows} color rows for LUT_3D_SIZE ${size}, but got ${dataRows}`,
);
}
const data = new Float32Array(expectedRows * 4);
let dataIndex = 0;
for (let index = 0; index < lines.length; index++) {
const line = stripComment(lines[index]).trim();
if (line.length === 0) {
continue;
}
const [keyword, ...values] = line.split(/\s+/);
if (HEADER_DIRECTIVES.has(keyword)) {
continue;
}
View on GitHub (pinned to 46a3a6bf13)