{"record":{"id":"d3de4937ac25ce6d","repo":"material-components/material-components-android","slug":"motion-easing-control-point-value-must-be-between","errorCode":null,"errorMessage":"Motion easing control point value must be between 0 and 1; instead got: %s","messagePattern":"Motion easing control point value must be between 0 and 1; instead got: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"lib/java/com/google/android/material/motion/MotionUtils.java","lineNumber":177,"sourceCode":"    return isLegacyEasingType(easingString, EASING_TYPE_CUBIC_BEZIER)\n        || isLegacyEasingType(easingString, EASING_TYPE_PATH);\n  }\n\n  private static boolean isLegacyEasingType(String easingString, String easingType) {\n    return easingString.startsWith(easingType + EASING_TYPE_FORMAT_START)\n        && easingString.endsWith(EASING_TYPE_FORMAT_END);\n  }\n\n  private static String getLegacyEasingContent(String easingString, String easingType) {\n    return easingString.substring(\n        easingType.length() + EASING_TYPE_FORMAT_START.length(),\n        easingString.length() - EASING_TYPE_FORMAT_END.length());\n  }\n\n  private static float getLegacyControlPoint(String[] controlPoints, int index) {\n    float controlPoint = Float.parseFloat(controlPoints[index]);\n    if (controlPoint < 0 || controlPoint > 1) {\n      throw new IllegalArgumentException(\n          \"Motion easing control point value must be between 0 and 1; instead got: \"\n              + controlPoint);\n    }\n    return controlPoint;\n  }\n}\n","sourceCodeStart":159,"sourceCodeEnd":184,"githubUrl":"https://github.com/material-components/material-components-android/blob/ac7e18efeefb331850c561faf9ab8bf81d27ba68/lib/java/com/google/android/material/motion/MotionUtils.java#L159-L184","documentation":"When parsing control points from a legacy cubic-bezier easing string, getLegacyControlPoint requires each of the four floats to be within [0, 1]; a value outside that range throws IllegalArgumentException including the offending number. This matches PathInterpolator's own constraint on control points (x-axis especially), enforced earlier with a clearer message.","triggerScenarios":"A theme string like 'cubic-bezier(1.2, 0, 0.2, 1)' or a negative control point resolved through resolveThemeInterpolator; Float.parseFloat succeeds but the range check fails.","commonSituations":"Copying CSS curves with y-values outside [0,1] (legal in CSS for overshoot, illegal here); negative values intended as anticipation; locale issues where a decimal comma breaks parsing into a different number.","solutions":["Clamp all four control point values to the [0, 1] range","For overshoot/elastic curves, use an @interpolator resource built from a PathInterpolator XML (paths may still be constrained, so prefer overshoot interpolators)","Use built-in material easing defaults for standard motion"],"exampleFix":"<!-- before -->\n<item name=\"motionEasingEmphasized\">cubic-bezier(0.2, 0, 0, 1.3)</item>\n<!-- after -->\n<item name=\"motionEasingEmphasized\">cubic-bezier(0.2, 0, 0, 1)</item>","handlingStrategy":"validation","validationCode":"fun clampControlPoints(points: FloatArray): FloatArray =\n    FloatArray(points.size) { i -> points[i].coerceIn(0f, 1f) }","typeGuard":null,"tryCatchPattern":"catch (e: IllegalArgumentException) { Log.w(TAG, \"Control point out of range; clamping\", e); useClampedCurve() }","preventionTips":["Keep all four bezier control points in [0, 1]","For overshoot motion use an overshoot interpolator resource instead of out-of-range y values","Validate easing constants in unit tests"],"tags":["android","material-components","animation","easing","cubic-bezier","validation"],"backgroundTag":null,"analyzedSha":"ac7e18efeefb331850c561faf9ab8bf81d27ba68","analyzedAt":"2026-08-14T15:35:35.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}