remotion-dev/remotion · error · TypeError

extrapolateLeft: "identity" is not supported for non-numeric

Error message

extrapolateLeft: "identity" is not supported for non-numeric strings

What it means

Thrown by interpolateDiscreteString when input is below inputRange[0] and options.extrapolateLeft is 'identity'. Identity extrapolation would return the raw (sub-min) input as a number, but a discrete non-numeric string outputRange has no numeric value to return, so the combination is rejected.

Source

Thrown at packages/core/src/interpolate.ts:914

			}) !== Easing.step1
		) {
			throw new TypeError(
				'Non-numeric strings can only be interpolated using Easing.step1',
			);
		}
	}

	const posterizedInput =
		options?.posterize === undefined
			? input
			: Math.floor(input / options.posterize) * options.posterize;
	const inputMin = inputRange[0];
	const inputMax = inputRange[inputRange.length - 1];
	let resolvedInput = posterizedInput;

	if (resolvedInput < inputMin) {
		if (options?.extrapolateLeft === 'identity') {
			throw new TypeError(
				'extrapolateLeft: "identity" is not supported for non-numeric strings',
			);
		}

		if (options?.extrapolateLeft === 'wrap') {
			const wrapRange = inputMax - inputMin;
			resolvedInput =
				((((resolvedInput - inputMin) % wrapRange) + wrapRange) % wrapRange) +
				inputMin;
		} else {
			return outputRange[0];
		}
	}

	if (resolvedInput > inputMax) {
		if (options?.extrapolateRight === 'identity') {
			throw new TypeError(
				'extrapolateRight: "identity" is not supported for non-numeric strings',

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Use extrapolateLeft: 'clamp' (default discrete behavior returns the first output) or 'extend' for non-numeric strings.
  2. Guard the input so it never falls below inputRange[0].
  3. Switch to extrapolateLeft: 'wrap' to cycle through outputs.

Example fix

// before
interpolate(t, [0, 1], ["red", "blue"], {
  easing: Easing.step1,
  extrapolateLeft: 'identity',
});
// after
interpolate(t, [0, 1], ["red", "blue"], {
  easing: Easing.step1,
  extrapolateLeft: 'clamp',
});
Defensive patterns

Strategy: validation

Validate before calling

function isDiscreteStringRange(outputRange: readonly (string | number)[]): boolean {
  return outputRange.some((o) => typeof o === 'string'
    && o.trim().split(/\s+/).some((p) => !/^([+-]?(?:\d+\.?\d*|\.\d+))([a-zA-Z%]+)?$/.test(p)
      && !/^(left|right|top|bottom|center)$/i.test(p)));
}

if (isDiscreteStringRange(outputRange) && options?.extrapolateLeft === 'identity') {
  throw new Error('Use extrapolateLeft: clamp|extend|wrap for discrete strings');
}

Prevention

When it happens

Trigger: interpolate(t, [0, 1], ["red", "blue"], {easing: Easing.step1, extrapolateLeft: 'identity'}) called with t < 0.

Common situations: Reusing options from a numeric interpolate() call (where 'identity' is valid) on a discrete string call; animating a property whose input can undershoot the range.

Related errors


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