remotion-dev/remotion · error · TypeError

"${name}" must be a hex color such as "#000000"

Error message

"${name}" must be a hex color such as "#000000"

What it means

`parseHexColor` runs on `resolved.colorA`, `resolved.colorB`, and `resolved.backgroundColor` after `resolve()`. It requires a `#RRGGBB` hex string (with or without the leading `#`, case-insensitive, exactly 6 hex digits). The regex `/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i` rejects 3-digit shorthand, `rgba()`, named colors, and `#RRGGBBAA`.

Source

Thrown at packages/brand/src/effects/metallic-swirl-effect.ts:313

const assertOptionalEnum = <T extends string>(
	value: unknown,
	name: keyof MetallicSwirlParams,
	variants: readonly T[],
): void => {
	if (value === undefined) {
		return;
	}

	if (typeof value !== 'string' || !variants.includes(value as T)) {
		throw new TypeError(`"${name}" must be one of ${variants.join(', ')}`);
	}
};

const parseHexColor = (hex: string, name: keyof MetallicSwirlParams): Rgb => {
	const match = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
	if (!match) {
		throw new TypeError(`"${name}" must be a hex color such as "#000000"`);
	}

	return [
		parseInt(match[1], 16) / 255,
		parseInt(match[2], 16) / 255,
		parseInt(match[3], 16) / 255,
	];
};

const validateMetallicSwirlParams = (
	params: MetallicSwirlParams = {},
): void => {
	assertParamsObject(params, 'Metallic swirl');
	assertOptionalFiniteNumber(params.time, 'time');
	assertOptionalFiniteNumber(params.speed, 'speed');
	assertOptionalFiniteNumber(params.zoom, 'zoom');
	assertOptionalFiniteNumber(params.iterations, 'iterations');
	assertOptionalFiniteNumber(params.sampleGap, 'sampleGap');

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Expand to 6-digit hex: `'#fff'` → `'#ffffff'`.
  2. Convert named colors and rgb()/hsl() strings to `#RRGGBB` before passing.
  3. Strip any alpha channel into the separate `opacity` field.
  4. Validate with the same regex at your input boundary.

Example fix

// before
metallicSwirl({colorA: '#fff', backgroundColor: 'black'});

// after
metallicSwirl({colorA: '#ffffff', backgroundColor: '#000000'});
Defensive patterns

Strategy: validation

Validate before calling

// Validate / normalize color strings to #RRGGBB before passing.
const HEX6 = /^#?([a-f\d]{6})$/i;
const toHex6 = (v: unknown): string => {
  if (typeof v !== 'string') {
    throw new Error('color must be a string');
  }
  const trimmed = v.trim();
  if (HEX6.test(trimmed)) {
    return trimmed.startsWith('#') ? trimmed : `#${trimmed}`;
  }
  // expand 3-digit shorthand
  const short = /^#?([a-f\d]{3})$/i.exec(trimmed);
  if (short) {
    const [, d] = short;
    return `#${d.split('').map((c) => c + c).join('')}`;
  }
  throw new Error(`color must be #RRGGBB hex, got ${JSON.stringify(v)}`);
};
metallicSwirl({colorA: toHex6(raw.colorA)});

Type guard

const isHex6Color = (v: unknown): v is string =>
  typeof v === 'string' && /^#?[a-f\d]{6}$/i.test(v.trim());

Prevention

When it happens

Trigger: `metallicSwirl({colorA: 'red'})`, `metallicSwirl({colorA: '#fff'})` (3-digit), `metallicSwirl({backgroundColor: 'rgb(0,0,0)'})`, `metallicSwirl({colorA: '#ff0038ff'})` (8-digit), `metallicSwirl({colorA: '00000x'})`.

Common situations: Using 3-digit shorthand from design tokens; passing CSS named colors; alpha-channel hex (`#RRGGBBAA`); color picker outputs that default to rgb()/hsl() strings.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/9a9a8c2882bb006d. Report an issue: GitHub.