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

Motion easing theme attribute must be an @interpolator resou

Error message

Motion easing theme attribute must be an @interpolator resource for ?attr/motionEasing*Interpolator attributes or a string for ?attr/motionEasing* attributes.

What it means

resolveThemeInterpolator accepts ?attr/motionEasing* theme attributes in two shapes: a string (legacy programmatic easing format like cubic-bezier(...) or pathData) or an @interpolator resource for the *Interpolator variants. If the resolved TypedValue type is anything other than TYPE_STRING, the attribute is neither of those shapes and the method throws IllegalArgumentException.

Source

Thrown at lib/java/com/google/android/material/motion/MotionUtils.java:120

   * @param context context from where the theme attribute will be resolved
   * @param attrResId the {@code motionEasing*} theme attribute to resolve
   * @param defaultInterpolator the interpolator to be returned if unable to resolve {@code
   *     attrResId}.
   * @return the resolved {@link TimeInterpolator} which {@code attrResId} points to or the {@code
   *     defaultInterpolator} if resolution was unsuccessful.
   */
  @NonNull
  public static TimeInterpolator resolveThemeInterpolator(
      @NonNull Context context,
      @AttrRes int attrResId,
      @NonNull TimeInterpolator defaultInterpolator) {
    TypedValue easingValue = new TypedValue();
    if (!context.getTheme().resolveAttribute(attrResId, easingValue, true)) {
      return defaultInterpolator;
    }

    if (easingValue.type != TypedValue.TYPE_STRING) {
      throw new IllegalArgumentException(
          "Motion easing theme attribute must be an @interpolator resource for"
              + " ?attr/motionEasing*Interpolator attributes or a string for"
              + " ?attr/motionEasing* attributes.");
    }

    String easingString = String.valueOf(easingValue.string);
    if (isLegacyEasingAttribute(easingString)) {
      return getLegacyThemeInterpolator(easingString);
    }

    return AnimationUtils.loadInterpolator(context, easingValue.resourceId);
  }

  private static TimeInterpolator getLegacyThemeInterpolator(String easingString) {
    if (isLegacyEasingType(easingString, EASING_TYPE_CUBIC_BEZIER)) {
      String controlPointsString = getLegacyEasingContent(easingString, EASING_TYPE_CUBIC_BEZIER);
      String[] controlPoints = controlPointsString.split(",");
      if (controlPoints.length != 4) {

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Point the ?attr/motionEasing* attribute at an @interpolator resource (for *Interpolator variants)
  2. Or use a string value in a recognized format: 'cubic-bezier(x1,y1,x2,y2)' or 'pathData:...'
  3. Double-check the attribute name variant matches the value type being set

Example fix

<!-- before -->
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
  <item name="motionEasingStandardInterpolator">@integer/my_easing</item>
</style>
<!-- after -->
<style name="Theme.MyApp" parent="Theme.Material3.DayNight">
  <item name="motionEasingStandardInterpolator">@interpolator/mtrl_fast_out_slow_in</item>
</style>
Defensive patterns

Strategy: validation

Validate before calling

// Verify the theme attribute resolves to a string or interpolator before use
fun safeResolveInterpolator(context: Context, attr: Int, default: TimeInterpolator): TimeInterpolator {
    val tv = TypedValue()
    if (!context.theme.resolveAttribute(attr, tv, true)) return default
    return if (tv.type == TypedValue.TYPE_STRING) {
        try { MotionUtils.resolveThemeInterpolator(context, attr, default) } catch (e: Exception) { default }
    } else default
}

Try / catch

catch (e: IllegalArgumentException) { Log.w(TAG, "Bad motionEasing attribute; falling back to default interpolator", e); useDefaultInterpolator() }

Prevention

When it happens

Trigger: Setting a ?attr/motionEasing* attribute to a non-string, non-interpolator resource type — e.g. a color, dimension, integer, or boolean — in a theme; resolveThemeInterpolator then reads easingValue.type != TYPE_STRING and throws.

Common situations: Copy-pasting theme overrides and assigning a reference to a non-interpolator resource; confusing motionEasingLinear (string format) with motionEasingLinearInterpolator (@interpolator); migration from string easings to interpolator resources with the wrong target.

Related errors


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