material-components/material-components-android · error · IllegalStateException

The stepSize(%s) must be 0, or a factor of the valueFrom(%s)

Error message

The stepSize(%s) must be 0, or a factor of the valueFrom(%s)-valueTo(%s) range

What it means

BaseSlider.validateStepSize() throws IllegalStateException when stepSize > 0 and valueTo is not an exact multiple of stepSize (checked with BigDecimal to avoid float rounding errors, i.e. valueLandsOnTick(valueTo) is false). The tick system requires the range valueFrom..valueTo to divide evenly into steps, otherwise ticks cannot be laid out consistently across the range.

Source

Thrown at lib/java/com/google/android/material/slider/BaseSlider.java:732

            .subtract(new BigDecimal(Float.toString(valueFrom)), DECIMAL64)
            .doubleValue();
    return isMultipleOfStepSize(result);
  }

  private boolean isMultipleOfStepSize(double value) {
    // We're using BigDecimal here to avoid floating point rounding errors.
    double result =
        new BigDecimal(Double.toString(value))
            .divide(new BigDecimal(Float.toString(stepSize)), DECIMAL64)
            .doubleValue();

    // If the result is a whole number, it means the value is a multiple of stepSize.
    return Math.abs(Math.round(result) - result) < THRESHOLD;
  }

  private void validateStepSize() {
    if (stepSize > 0.0f && !valueLandsOnTick(valueTo)) {
      throw new IllegalStateException(
          String.format(EXCEPTION_ILLEGAL_STEP_SIZE, stepSize, valueFrom, valueTo));
    }
  }

  private void validateValues() {
    if (valueFrom >= valueTo) {
      throw new IllegalStateException(
          String.format(EXCEPTION_ILLEGAL_VALUE_FROM, valueFrom, valueTo));
    }

    for (Float value : values) {
      if (value < valueFrom || value > valueTo) {
        throw new IllegalStateException(
            String.format(EXCEPTION_ILLEGAL_VALUE, value, valueFrom, valueTo));
      }
      if (stepSize > 0.0f && !valueLandsOnTick(value)) {
        throw new IllegalStateException(
            String.format(EXCEPTION_ILLEGAL_DISCRETE_VALUE, value, valueFrom, stepSize, stepSize));

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Adjust valueTo (or valueFrom) so the range is an exact multiple of stepSize — e.g. valueFrom=0, valueTo=10, stepSize=0.5.
  2. Or compute stepSize from the range at runtime so it always divides evenly: pick stepSize = range / n.
  3. Or set stepSize=0 to disable ticks entirely when a continuous slider is acceptable.
  4. Prefer integer-based ranges and derive display strings, avoiding float division pitfalls.

Example fix

<!-- before -->
<com.google.android.material.slider.Slider
    android:valueFrom="0" android:valueTo="10.3" app:stepSize="0.5" />

<!-- after -->
<com.google.android.material.slider.Slider
    android:valueFrom="0" android:valueTo="10.5" app:stepSize="0.5" />
Defensive patterns

Strategy: validation

Validate before calling

static boolean rangeDividesByStep(float from, float to, float step) {
  if (step <= 0f) return true;
  java.math.BigDecimal r = new java.math.BigDecimal(Float.toString(to - from))
      .divide(new java.math.BigDecimal(Float.toString(step)),
              java.math.MathContext.DECIMAL64);
  double result = r.doubleValue();
  return Math.abs(Math.round(result) - result) < 1e-4;
}
// usage
if (rangeDividesByStep(valueFrom, valueTo, stepSize)) slider.setStepSize(stepSize);

Prevention

When it happens

Trigger: Setting app:stepSize="0.5" with valueFrom="0" and valueTo="10.3" (10.3/0.5 = 20.6, not whole); or calling setStepSize(...) at runtime with a value that doesn't divide the configured range; using float literals whose binary representation makes the division non-integral.

Common situations: Hardcoding valueTo from data (e.g. max price 10.3) while keeping a neat step like 1 or 0.5; letting a backend value define the range without adjusting stepSize; changing valueTo dynamically (slider bounds from filtering) while leaving stepSize fixed.

Related errors


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