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

Not enough labels(%d) to display all the values(%d)

Error message

Not enough labels(%d) to display all the values(%d)

What it means

IllegalStateException thrown in the label-assignment loop when the internal label pool runs out before every thumb (ending with the focused thumb) got a label. Labels are created lazily into a pool sized by the slider itself, so in practice this is an internal invariant violation — most often seen when values were mutated without the label pool being rebuilt, or in custom subclasses that interfere with label handling.

Source

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

      labelsInAnimator = createLabelAnimator(true);
      labelsOutAnimator = null;
      labelsInAnimator.start();
    }

    Iterator<TooltipDrawable> labelItr = labels.iterator();

    if (showLabelOnAllThumbs) {
      for (int i = 0; i < values.size() && labelItr.hasNext(); i++) {
        if (i == focusedThumbIdx) {
          // We position the focused thumb last so it's displayed on top, so skip it for now.
          continue;
        }
        setValueForLabel(labelItr.next(), values.get(i));
      }
    }

    if (!labelItr.hasNext()) {
      throw new IllegalStateException(
          String.format(
              "Not enough labels(%d) to display all the values(%d)", labels.size(), values.size()));
    }

    // Now set the label for the focused thumb so it's on top.
    setValueForLabel(labelItr.next(), values.get(focusedThumbIdx));
  }

  private String formatValue(float value) {
    if (hasLabelFormatter()) {
      return formatter.getFormattedValue(value);
    }

    return String.format((int) value == value ? "%.0f" : "%.2f", value);
  }

  private void setValueForLabel(TooltipDrawable label, float value) {
    label.setText(formatValue(value));

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Never mutate the list returned by getValues(); always call setValues() with a new list.
  2. In custom subclasses, delegate value changes to setValues() so dirtyConfig and the label pool stay consistent.
  3. If it reproduces with stock Slider, upgrade (or pin) the Material Components version and report it with the exact configuration.

Example fix

// before
slider.getValues().add(5f); // in-place mutation desyncs internal label count

// after
List<Float> vals = new ArrayList<>(slider.getValues());
vals.add(5f);
slider.setValues(vals);
Defensive patterns

Strategy: try-catch

Validate before calling

List<Float> vals = new ArrayList<>(slider.getValues());
vals.add(newValue);
slider.setValues(vals); // keeps label pool in sync with value count

Try / catch

Wrap interaction-triggered show paths defensively in custom subclasses: try { slider.onDraw(...) } catch (IllegalStateException e) { Log.e(TAG, "label/value desync", e); } — but fix the root cause (never mutate getValues() in place).

Prevention

When it happens

Trigger: Having more thumbs/values than pooled labels after in-place mutation of the values list (e.g. obtaining getValues() and modifying it), or subclassing BaseSlider and overriding label behavior/values handling inconsistently.

Common situations: Custom slider subclasses (e.g. custom RangeSlider variants) that bypass setValues(); rare version-specific regressions in the label pool logic — check the Material Components changelog if it reproduces with plain Slider usage.

Related errors


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