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

currentPage cannot be after lastPage

Error message

currentPage cannot be after lastPage

What it means

MonthsPagerAdapter validates that the month the date picker opens at (CalendarConstraints.getOpenAt(), the currentPage) does not fall after the last selectable month (getEnd(), lastPage). An openAt beyond end would place the initial page outside the legal paging range, so the constructor throws IllegalArgumentException.

Source

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

  @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);
  }

  public static class ViewHolder extends RecyclerView.ViewHolder {

View on GitHub (pinned to ac7e18efee)

Solutions

  1. Clamp openAt into the range: openAt = Math.max(start, Math.min(end, openAt)) before calling setOpenAt().
  2. If the picker should open at the latest allowed month, just omit setOpenAt() or set it equal to end.
  3. Verify setEnd() receives milliseconds and a value >= setStart(); log the three timestamps during development to catch unit mix-ups.

Example fix

// before
new CalendarConstraints.Builder()
    .setStart(start)
    .setOpenAt(target) // target > end -> crash
    .setEnd(end)
    .build();

// after
new CalendarConstraints.Builder()
    .setStart(start)
    .setOpenAt(Math.min(target, end))
    .setEnd(end)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

long openAtClamped = Math.max(startMillis, Math.min(endMillis, openAtMillis));
CalendarConstraints constraints = new CalendarConstraints.Builder()
    .setStart(startMillis).setEnd(endMillis).setOpenAt(openAtClamped).build();

Try / catch

try { pickerBuilder.setCalendarConstraints(constraints).build(); } catch (IllegalArgumentException e) { constraints = new CalendarConstraints.Builder().setStart(start).setEnd(end).build(); /* no openAt */ }

Prevention

When it happens

Trigger: CalendarConstraints.Builder().setOpenAt(openMs).setEnd(endMs) with openMs in a Month after endMs (e.g. openAt = December 2026, end = June 2026). Also triggered when end is defaulted to today's month but openAt is set to a future month, or when end/openAt are swapped by mistake.

Common situations: Letting users pick a future 'open at' month while capping end at today; timezone differences pushing a month boundary; refactors that reorder builder calls and accidentally exchange the end and openAt arguments.

Related errors


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