remotion-dev/remotion · error · TypeError

"backgroundColor" must be a string

Error message

"backgroundColor" must be a string

What it means

`validateMetallicSwirlParams` checks `backgroundColor` separately from the generic color parser: if it is present (`!== undefined`) it must be a `string`, otherwise this throws. Note this runs before `parseHexColor`, so a non-string fails here first. A string that is not valid hex instead fails at error 30.

Source

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

	assertOptionalFiniteNumber(params.zoom, 'zoom');
	assertOptionalFiniteNumber(params.iterations, 'iterations');
	assertOptionalFiniteNumber(params.sampleGap, 'sampleGap');
	assertOptionalFiniteNumber(params.tangentForce, 'tangentForce');
	assertOptionalFiniteNumber(params.gradientForce, 'gradientForce');
	assertOptionalFiniteNumber(params.colorPhaseR, 'colorPhaseR');
	assertOptionalFiniteNumber(params.colorPhaseG, 'colorPhaseG');
	assertOptionalFiniteNumber(params.colorPhaseB, 'colorPhaseB');
	assertOptionalFiniteNumber(params.colorRange, 'colorRange');
	assertOptionalFiniteNumber(params.colorBias, 'colorBias');
	assertOptionalFiniteNumber(params.brightness, 'brightness');
	assertOptionalFiniteNumber(params.opacity, 'opacity');
	assertOptionalEnum(params.mode, 'mode', MODES);

	if (
		params.backgroundColor !== undefined &&
		typeof params.backgroundColor !== 'string'
	) {
		throw new TypeError('"backgroundColor" must be a string');
	}

	if (params.colorA !== undefined && typeof params.colorA !== 'string') {
		throw new TypeError('"colorA" must be a string');
	}

	if (params.colorB !== undefined && typeof params.colorB !== 'string') {
		throw new TypeError('"colorB" must be a string');
	}

	const resolved = resolve(params);
	validateRange(resolved.speed, 'speed', 0, 10);
	validateRange(resolved.zoom, 'zoom', 0.01, 50);
	validateRange(resolved.iterations, 'iterations', 1, 12);
	validateRange(resolved.sampleGap, 'sampleGap', 0.0001, 1);
	validateRange(resolved.colorRange, 'colorRange', 0, 2);
	validateRange(resolved.colorBias, 'colorBias', 0, 2);
	validateRange(resolved.brightness, 'brightness', 0, 5);

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pass a hex string: `metallicSwirl({backgroundColor: '#000000'})`.
  2. Omit the key entirely to use the default (`#000000`).
  3. Convert numeric codes to `#RRGGBB` at your boundary.
  4. Use `undefined`, not `null`, for 'unset'.

Example fix

// before
metallicSwirl({backgroundColor: 0x000000});

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

Strategy: type-guard

Validate before calling

// Ensure backgroundColor is a string (or omit it).
const optionalString = (v: unknown): string | undefined => {
  if (v === undefined || v === null) return undefined;
  if (typeof v !== 'string') {
    throw new Error('backgroundColor must be a string');
  }
  return v;
};
metallicSwirl({backgroundColor: optionalString(raw.backgroundColor)});

Type guard

const isOptionalString = (v: unknown): v is string =>
  typeof v === 'string';

Prevention

When it happens

Trigger: `metallicSwirl({backgroundColor: 0})`, `metallicSwirl({backgroundColor: null})`, `metallicSwirl({backgroundColor: ['#000']})`, `metallicSwirl({backgroundColor: true})`.

Common situations: Passing a numeric color code (0x000000) from a different system; null used to mean 'unset'; array from a multi-value form field; boolean toggles.

Related errors


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