Popmotion/popmotion · error

Array of easing functions must be of length `input.length -

Error message

Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.

What it means

When the `ease` option is an array of easing functions, `interpolate` requires exactly `input.length - 1` easers, because easings apply to the transitions *between* consecutive input stops, not to the stops themselves. `invariant` throws when the array is longer or shorter. A single easing function (non-array) is fine and is applied to all segments.

Source

Thrown at packages/popmotion/src/utils/interpolate.ts:130

 *
 * mixColor(0.5) // 'rgba(128, 128, 128, 1)'
 * ```
 *
 * @public
 */
export function interpolate<T>(
  input: number[],
  output: T[],
  { clamp: isClamp = true, ease, mixer }: InterpolateOptions<T> = {}
) {
  const inputLength = input.length;

  invariant(
    inputLength === output.length,
    'Both input and output ranges must be the same length'
  );

  invariant(
    !ease || !Array.isArray(ease) || ease.length === inputLength - 1,
    'Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.'
  );

  // If input runs highest -> lowest, reverse both arrays
  if (input[0] > input[inputLength - 1]) {
    input = [].concat(input);
    output = [].concat(output);
    input.reverse();
    output.reverse();
  }

  const mixers = createMixers(output, ease, mixer);

  const interpolator =
    inputLength === 2
      ? fastInterpolate(input, mixers)
      : slowInterpolate(input, mixers);

View on GitHub (pinned to adf681efd8)

Solutions

  1. Make `ease.length === input.length - 1` (one easing per segment between stops)
  2. If you want the same easing everywhere, pass a single function instead of an array: `ease: easeInOut`
  3. Count segments as `input.length - 1`, not `input.length`
  4. Validate the ease array length against the input range before calling

Example fix

// before
interpolate([0, 0.5, 1], [0, 100, 200], { ease: [linear, linear, linear] });
// after
interpolate([0, 0.5, 1], [0, 100, 200], { ease: [linear, linear] });
Defensive patterns

Strategy: validation

Validate before calling

function validateEase(input, ease) {
  if (Array.isArray(ease) && ease.length !== input.length - 1) {
    throw new Error(`ease array must have ${input.length - 1} items (segments), got ${ease.length}`);
  }
}
// call before interpolate(input, output, { ease })

Type guard

function isValidEaseArray<T>(input: number[], ease: unknown): ease is ((t: number) => T)[] {
  return Array.isArray(ease) && ease.length === input.length - 1 && ease.every(fn => typeof fn === 'function');
}

Prevention

When it happens

Trigger: `interpolate([0, 0.5, 1], ['0px','100px','200px'], { ease: [linear, linear, linear] })` (3 easers for 2 segments), or `{ ease: [linear] }` for a 3-stop input, or building the ease array dynamically and miscounting segments.

Common situations: Off-by-one confusion: developers assume one easing per input value instead of one per segment; copying an ease array from a config with a different number of keyframes; programmatic ease generation.

Related errors


AI-assisted analysis of Popmotion/popmotion@adf681efd8 (2026-09-02). Data as JSON: /api/errors/3cb0a9c5b9a87644. Report an issue: GitHub.