{"record":{"id":"951451348c0e5e65","repo":"material-components/material-components-android","slug":"motion-easing-theme-attribute-must-be-an-interpol","errorCode":null,"errorMessage":"Motion easing theme attribute must be an @interpolator resource for ?attr/motionEasing*Interpolator attributes or a string for ?attr/motionEasing* attributes.","messagePattern":"Motion easing theme attribute must be an @interpolator resource for \\?attr/motionEasing\\*Interpolator attributes or a string for \\?attr/motionEasing\\* attributes\\.","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"lib/java/com/google/android/material/motion/MotionUtils.java","lineNumber":120,"sourceCode":"   * @param context context from where the theme attribute will be resolved\n   * @param attrResId the {@code motionEasing*} theme attribute to resolve\n   * @param defaultInterpolator the interpolator to be returned if unable to resolve {@code\n   *     attrResId}.\n   * @return the resolved {@link TimeInterpolator} which {@code attrResId} points to or the {@code\n   *     defaultInterpolator} if resolution was unsuccessful.\n   */\n  @NonNull\n  public static TimeInterpolator resolveThemeInterpolator(\n      @NonNull Context context,\n      @AttrRes int attrResId,\n      @NonNull TimeInterpolator defaultInterpolator) {\n    TypedValue easingValue = new TypedValue();\n    if (!context.getTheme().resolveAttribute(attrResId, easingValue, true)) {\n      return defaultInterpolator;\n    }\n\n    if (easingValue.type != TypedValue.TYPE_STRING) {\n      throw new IllegalArgumentException(\n          \"Motion easing theme attribute must be an @interpolator resource for\"\n              + \" ?attr/motionEasing*Interpolator attributes or a string for\"\n              + \" ?attr/motionEasing* attributes.\");\n    }\n\n    String easingString = String.valueOf(easingValue.string);\n    if (isLegacyEasingAttribute(easingString)) {\n      return getLegacyThemeInterpolator(easingString);\n    }\n\n    return AnimationUtils.loadInterpolator(context, easingValue.resourceId);\n  }\n\n  private static TimeInterpolator getLegacyThemeInterpolator(String easingString) {\n    if (isLegacyEasingType(easingString, EASING_TYPE_CUBIC_BEZIER)) {\n      String controlPointsString = getLegacyEasingContent(easingString, EASING_TYPE_CUBIC_BEZIER);\n      String[] controlPoints = controlPointsString.split(\",\");\n      if (controlPoints.length != 4) {","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/material-components/material-components-android/blob/ac7e18efeefb331850c561faf9ab8bf81d27ba68/lib/java/com/google/android/material/motion/MotionUtils.java#L102-L138","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Point the ?attr/motionEasing* attribute at an @interpolator resource (for *Interpolator variants)","Or use a string value in a recognized format: 'cubic-bezier(x1,y1,x2,y2)' or 'pathData:...'","Double-check the attribute name variant matches the value type being set"],"exampleFix":"<!-- before -->\n<style name=\"Theme.MyApp\" parent=\"Theme.Material3.DayNight\">\n  <item name=\"motionEasingStandardInterpolator\">@integer/my_easing</item>\n</style>\n<!-- after -->\n<style name=\"Theme.MyApp\" parent=\"Theme.Material3.DayNight\">\n  <item name=\"motionEasingStandardInterpolator\">@interpolator/mtrl_fast_out_slow_in</item>\n</style>","handlingStrategy":"validation","validationCode":"// Verify the theme attribute resolves to a string or interpolator before use\nfun safeResolveInterpolator(context: Context, attr: Int, default: TimeInterpolator): TimeInterpolator {\n    val tv = TypedValue()\n    if (!context.theme.resolveAttribute(attr, tv, true)) return default\n    return if (tv.type == TypedValue.TYPE_STRING) {\n        try { MotionUtils.resolveThemeInterpolator(context, attr, default) } catch (e: Exception) { default }\n    } else default\n}","typeGuard":null,"tryCatchPattern":"catch (e: IllegalArgumentException) { Log.w(TAG, \"Bad motionEasing attribute; falling back to default interpolator\", e); useDefaultInterpolator() }","preventionTips":["Use *Interpolator attribute variants only with @interpolator resources","Use plain motionEasing* attributes only with string easing formats","Prefer built-in material theme defaults unless a custom curve is truly needed"],"tags":["android","material-components","animation","interpolator","theme-attribute"],"backgroundTag":null,"analyzedSha":"ac7e18efeefb331850c561faf9ab8bf81d27ba68","analyzedAt":"2026-08-14T15:35:35.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}