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

firstPage cannot be after currentPage

Error message

firstPage cannot be after currentPage

What it means

MonthsPagerAdapter is the adapter that backs the month grid inside MaterialDatePicker. Its constructor validates that CalendarConstraints forms a sane page range: the start month (firstPage) must not be later than the month the picker is told to open at (currentPage, from calendarConstraints.getOpenAt()). If the openAt month falls before start, the pager would be asked to show a page outside the allowed bounds, so construction fails fast with IllegalArgumentException.

Source

Thrown at lib/java/com/google/android/material/datepicker/MonthsPagerAdapter.java:81

  private final OnDayClickListener onDayClickListener;
  @Nullable private final MaterialCalendar.OnMonthNavigationListener onMonthNavigationListener;
  private final int itemHeight;
  @Nullable private Month visibleMonth;
  @KeyboardFocusDirection private int keyboardFocusDirection = POSITION_UNSPECIFIED;

  MonthsPagerAdapter(
      @NonNull Context context,
      DateSelector<?> dateSelector,
      @NonNull CalendarConstraints calendarConstraints,
      @Nullable DayViewDecorator dayViewDecorator,
      OnDayClickListener onDayClickListener,
      @Nullable MaterialCalendar.OnMonthNavigationListener onMonthNavigationListener) {
    Month firstPage = calendarConstraints.getStart();
    Month lastPage = calendarConstraints.getEnd();
    Month currentPage = calendarConstraints.getOpenAt();

    if (firstPage.compareTo(currentPage) > 0) {
      throw new IllegalArgumentException("firstPage cannot be after currentPage");
    }
    if (currentPage.compareTo(lastPage) > 0) {
      throw new IllegalArgumentException("currentPage cannot be after lastPage");
    }

    int daysHeight = MonthAdapter.MAXIMUM_WEEKS * MaterialCalendar.getDayHeight(context);
    int labelHeight =
        MaterialDatePicker.isFullscreen(context) ? MaterialCalendar.getDayHeight(context) : 0;

    this.itemHeight = daysHeight + labelHeight;
    this.calendarConstraints = calendarConstraints;
    this.dateSelector = dateSelector;
    this.dayViewDecorator = dayViewDecorator;
    this.onDayClickListener = onDayClickListener;
    this.onMonthNavigationListener = onMonthNavigationListener;
    this.visibleMonth = currentPage;
    setHasStableIds(true);
  }

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Ensure CalendarConstraints openAt is within [start, end]: clamp openAt with openAt = Math.max(start, Math.min(end, openAt)) before building constraints.
  2. If you only need to constrain the selectable range, omit setOpenAt() entirely — the picker then defaults to opening at start (or today within range).
  3. Double-check timestamp units: CalendarConstraints.Builder expects milliseconds since epoch, not days or seconds.
  4. When deriving months from Calendar objects, clear smaller fields (DAY_OF_MONTH etc.) and compare the resulting TimeInMillis in the same timezone for all three values.

Example fix

// before
CalendarConstraints constraints = new CalendarConstraints.Builder()
    .setStart(startMillis)
    .setOpenAt(openMillis) // openMillis < startMillis -> crash
    .setEnd(endMillis)
    .build();

// after
long openAt = Math.max(startMillis, Math.min(endMillis, openMillis));
CalendarConstraints constraints = new CalendarConstraints.Builder()
    .setStart(startMillis)
    .setOpenAt(openAt)
    .setEnd(endMillis)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

CalendarConstraints.Builder b = new CalendarConstraints.Builder();
long start = ...; long end = ...; long openAt = ...;
if (openAt < start || openAt > end) {
  openAt = Math.max(start, Math.min(end, openAt));
}
b.setStart(start).setEnd(end).setOpenAt(openAt);
CalendarConstraints constraints = b.build();

Try / catch

try { new MaterialDatePicker.Builder<>(...).setCalendarConstraints(constraints).build(); } catch (IllegalArgumentException e) { /* clamp months and rebuild once; log the original range */ }

Prevention

When it happens

Trigger: Building a MaterialDatePicker with CalendarConstraints.Builder().setStart(startMs).setOpenAt(openMs) where openMs resolves to a Month strictly before startMs (e.g. start = March 2026, openAt = January 2026). Also happens when setStart() is called after set_openAt via setter chaining mistakes, or when timestamps are computed with wrong units (days vs milliseconds).

Common situations: Dynamically computing start/openAt from user profile data or server dates; copy-pasting epoch values with wrong magnitude; locale/timezone shifts that move a borderline timestamp into the previous month; upgrading picker versions where openAt semantics tightened.

Related errors


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