remotion-dev/remotion · error

Duplicate on line .

Error message

Duplicate ${keyword} on line ${index + 1}.

What it means

parseCubeLut collects header keywords into a set and rejects a .cube file that declares the same keyword twice, e.g. two TITLE or two LUT_3D_SIZE lines, since the parser could not decide which value is authoritative.

Solutions

  1. Remove the duplicate header line so each keyword appears exactly once.
  2. Keep only the intended TITLE / LUT_*_SIZE declaration and delete the other.
  3. Re-export the LUT from the source tool.
  4. Check git history if the file was recently merged.

Example fix

// before
TITLE "A"
TITLE "B"
// after
TITLE "A"
Defensive patterns

Strategy: validation

Validate before calling

const seen = new Set();
for (const line of headerLines) {
  const kw = line.split(/\s+/)[0];
  if (seen.has(kw)) throw new Error(`Duplicate keyword: ${kw}`);
  seen.add(kw);
}

Type guard

null

Try / catch

try {
  parseCubeLut(text);
} catch (err) {
  if (err.message.startsWith('Duplicate')) {
    // surface which keyword/line is duplicated
  }
}

Prevention

When it happens

Trigger: Loading a .cube file that contains a duplicated header keyword — like two TITLE lines or LUT_3D_SIZE declared twice — anywhere before color data.

Common situations: Concatenated or merge-conflicted .cube files; copy-paste editing that duplicated a header; buggy exporter emitting repeated headers.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18). Data as JSON: /api/errors/4a1dc56e07be981a. Report an issue: GitHub.

Appendix: source

Thrown at packages/studio/src/helpers/cube-lut.ts:39

	for (const [index, rawLine] of text.split(/\r\n?|\n/).entries()) {
		const line = rawLine.trim();
		if (!line || line.startsWith('#')) {
			continue;
		}

		const titleMatch = line.match(/^TITLE\s+"([^"]*)"\s*(?:#.*)?$/);
		const [keyword, ...values] = line.replace(/#.*$/, '').trim().split(/\s+/);
		const numbers = values.map(Number);
		const isHeader = /^[A-Z][A-Z0-9_]*$/.test(keyword);

		if (isHeader) {
			if (offset > 0) {
				throw new Error(`LUT header after color data on line ${index + 1}.`);
			}

			if (headers.has(keyword)) {
				throw new Error(`Duplicate ${keyword} on line ${index + 1}.`);
			}

			headers.add(keyword);
		}

		if (keyword === 'TITLE' && titleMatch) {
			title = titleMatch[1];
			continue;
		}

		if (keyword === 'LUT_1D_SIZE' || keyword === 'LUT_3D_SIZE') {
			if (type !== null) {
				throw new Error('Combined 1D and 3D LUTs are not supported.');
			}

			type = keyword === 'LUT_1D_SIZE' ? '1d' : '3d';
			size = numbers[0];
			const maxSize = type === '1d' ? 65536 : 256;

View on GitHub (pinned to 46a3a6bf13)