material-components/material-components-android · error · IllegalArgumentException

Keylines being linearly interpolated must have the same numb

Error message

Keylines being linearly interpolated must have the same number of keylines.

What it means

The second precondition in KeylineState.lerp (KeylineState.java:193): the two interpolated states must contain the same number of keylines, because lerping walks both lists index-by-index. Mismatched counts (with equal item sizes) throw IllegalArgumentException. Count mismatches arise when the two states were produced by different keyline configurations — e.g. different strategy outputs or different container sizes producing different keyline counts.

Source

Thrown at lib/java/com/google/android/material/carousel/KeylineState.java:193

  }

  /**
   * Linearly interpolate between two {@link KeylineState}s.
   *
   * @param from the start keyline state
   * @param to the end keyline state
   * @param progress the interpolation between from and to. When progress is 0, from will be
   *     returned. When progress is 1, to will be returned.
   */
  static KeylineState lerp(KeylineState from, KeylineState to, float progress) {
    if (from.getItemSize() != to.getItemSize()) {
      throw new IllegalArgumentException(
          "Keylines being linearly interpolated must have the same item size.");
    }
    List<Keyline> fromKeylines = from.getKeylines();
    List<Keyline> toKeylines = to.getKeylines();
    if (fromKeylines.size() != toKeylines.size()) {
      throw new IllegalArgumentException(
          "Keylines being linearly interpolated must have the same number of keylines.");
    }

    List<Keyline> keylines = new ArrayList<>();
    for (int i = 0; i < from.getKeylines().size(); i++) {
      keylines.add(Keyline.lerp(fromKeylines.get(i), toKeylines.get(i), progress));
    }

    int focalKeylineFirstIndex =
        AnimationUtils.lerp(
            from.getFirstFocalKeylineIndex(), to.getFirstFocalKeylineIndex(), progress);
    int focalKeylineLastIndex =
        AnimationUtils.lerp(
            from.getLastFocalKeylineIndex(), to.getLastFocalKeylineIndex(), progress);

    return new KeylineState(
        from.getItemSize(),
        keylines,

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Make the custom strategy's keyline count deterministic and stable across the two states being lerped — derive counts from the KeylineStateList/API contract (use KeylineState.Builder with addKeylineRange for stable counts).
  2. Rebuild both states from the same configuration before triggering transitions instead of reusing an old state.
  3. If strategy switching at runtime is required, prefer refreshing keylines without lerping (set new state directly) rather than animating between incompatible states.
Defensive patterns

Strategy: validation

Validate before calling

if (from.getKeylines().size() == to.getKeylines().size() && from.getItemSize() == to.getItemSize()) {
  KeylineState.lerp(from, to, progress);
}

Try / catch

try { KeylineState.lerp(from, to, progress); } catch (IllegalArgumentException e) { return to; // skip animation, snap to target }

Prevention

When it happens

Trigger: Library-internal lerping between a previous KeylineState (built before a configuration change) and a freshly calculated one whose addKeyline calls produced a different count — commonly caused by a custom CarouselStrategy whose keyline count depends on width, executed across a container resize, or by switching strategies at runtime.

Common situations: Custom strategies with count-varying math (e.g. 'fit as many keylines as width allows') returning different counts across rotations or fold/unfold transitions. Debug builds with different itemSpacing than release intermittently crashing only at specific widths. Like error 32, the crash appears during animation/measure, disconnected from the producing code.

Related errors


AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14). Data as JSON: /api/errors/6648680e6b5f189d. Report an issue: GitHub.