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

%1$s must have its Adapter set to a %2$s

Error message

%1$s must have its Adapter set to a %2$s

What it means

MaterialCalendarGridView (the grid inside MaterialDatePicker) requires its ListAdapter to be a MonthAdapter so it can drive day cells, selectors and navigation. setAdapter is final and throws IllegalArgumentException, formatted with the class names, when given any other adapter type. The message reads '%1$s must have its Adapter set to a %2$s' after String.format.

Source

Thrown at lib/java/com/google/android/material/datepicker/MaterialCalendarGridView.java:236

      // If no next focusable item in this month, return false to let the system move focus
      // out of the GridView.
      return false;
    }

    setSelection(nextPosition);
    return true;
  }

  @NonNull
  @Override
  public MonthAdapter getAdapter() {
    return (MonthAdapter) super.getAdapter();
  }

  @Override
  public final void setAdapter(ListAdapter adapter) {
    if (!(adapter instanceof MonthAdapter)) {
      throw new IllegalArgumentException(
          String.format(
              "%1$s must have its Adapter set to a %2$s",
              MaterialCalendarGridView.class.getCanonicalName(),
              MonthAdapter.class.getCanonicalName()));
    }
    super.setAdapter(adapter);
  }

  private void ensureFocusRingSelector(MonthAdapter monthAdapter) {
    Drawable selector = getSelector();
    if (selector instanceof FocusRingDrawable) {
      return;
    }

    Drawable drawable = FocusRingDrawable.wrap(getContext(), selector);
    if (drawable instanceof FocusRingDrawable) {
      FocusRingDrawable focusRingDrawable = (FocusRingDrawable) drawable;
      if (monthAdapter.calendarStyle != null) {

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Do not set a foreign adapter on MaterialCalendarGridView; let MaterialDatePicker manage it.
  2. If you subclass, subclass MonthAdapter and keep it compatible (it drives day rendering contracts).
  3. In test/tooling code that touches all adapters, skip views inside MaterialDatePicker.

Example fix

// before
gridView.adapter = MyCustomAdapter(items) // not a MonthAdapter -> throws

// after
class MyMonthAdapter(month: Month, selector: DateSelector<Long>) : MonthAdapter(month, selector)
gridView.adapter = MyMonthAdapter(month, selector)
Defensive patterns

Strategy: type-guard

Validate before calling

fun setAdapterSafely(grid: AbsListView, adapter: ListAdapter) {
  if (grid is MaterialCalendarGridView && adapter !is MonthAdapter) return
  grid.adapter = adapter
}

Type guard

fun supportsAdapter(grid: AbsListView, adapter: ListAdapter): Boolean =
    !(grid is MaterialCalendarGridView && adapter !is MonthAdapter)

Prevention

When it happens

Trigger: Calling gridView.setAdapter(anyNonMonthAdapter) on a MaterialCalendarGridView; subclassing MaterialDatePicker internals and injecting a custom adapter; reflection-based testing tools swapping adapters on arbitrary ListViews/GridViews.

Common situations: Test frameworks (Espresso/Robotium recorders, screenshot utils) that iterate all AbsListView instances and reset adapters; custom date picker forks that reuse the grid with their own adapter.

Related errors


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