remotion-dev/remotion · error · TypeError
"colorA" must be a string
Error message
"colorA" must be a string
What it means
`validateMetallicSwirlParams` checks `colorA`: if present (`!== undefined`) it must be a `string`, else this throws. Runs before `parseHexColor`, so non-strings fail here and invalid-hex strings fail at error 30. The two color slots (colorA, colorB) are checked independently.
Source
Thrown at packages/brand/src/effects/metallic-swirl-effect.ts:351
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);
validateRange(resolved.opacity, 'opacity', 0, 1);
parseHexColor(resolved.backgroundColor, 'backgroundColor');
parseHexColor(resolved.colorA, 'colorA');
parseHexColor(resolved.colorB, 'colorB');View on GitHub (pinned to 78fe4bb3fd)
Solutions
- Pass a hex string: `metallicSwirl({colorA: '#ff0038'})`.
- Omit the key to use the default color A.
- Convert RGB tuples to `#RRGGBB` at your boundary.
- Use `undefined`, not `null`, for 'unset'.
Example fix
// before
metallicSwirl({colorA: [255, 0, 56]});
// after
const toHex = (r, g, b) => '#' + [r, g, b].map(n => n.toString(16).padStart(2, '0')).join('');
metallicSwirl({colorA: toHex(255, 0, 56)}); Defensive patterns
Strategy: type-guard
Validate before calling
const optionalString = (v: unknown): string | undefined => {
if (v === undefined || v === null) return undefined;
if (typeof v !== 'string') {
throw new Error('colorA must be a string');
}
return v;
};
metallicSwirl({colorA: optionalString(raw.colorA)}); Type guard
const isOptionalString = (v: unknown): v is string => typeof v === 'string';
Prevention
- Pass hex strings for colorA; convert RGB tuples to `#RRGGBB` first.
- Omit the key to use the default colorA instead of passing null.
- Type params as `MetallicSwirlParams` so non-strings fail at compile time.
- Keep color picker output as hex strings end-to-end.
When it happens
Trigger: `metallicSwirl({colorA: 0xff0038})`, `metallicSwirl({colorA: null})`, `metallicSwirl({colorA: [255, 0, 56]})`, `metallicSwirl({colorA: true})`.
Common situations: Numeric color codes from design systems; RGB tuples/arrays from color pickers; null meaning 'unset'; boolean flags.
Related errors
- "backgroundColor" must be a string
- "colorB" must be a string
- ${name} params must be an object
- "${name}" must be a finite number
- "${name}" must be between ${min} and ${max}
AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12).
Data as JSON: /api/errors/770d6b63d23d398c.
Report an issue: GitHub.