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
- Ensure CalendarConstraints openAt is within [start, end]: clamp openAt with openAt = Math.max(start, Math.min(end, openAt)) before building constraints.
- If you only need to constrain the selectable range, omit setOpenAt() entirely — the picker then defaults to opening at start (or today within range).
- Double-check timestamp units: CalendarConstraints.Builder expects milliseconds since epoch, not days or seconds.
- 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
- Centralize CalendarConstraints construction in one factory that clamps openAt into [start, end].
- Always pass milliseconds since epoch; add unit tests asserting the three timestamps are ordered.
- Normalize all month timestamps through the same Calendar/timezone helper before comparing.
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
- currentPage cannot be after lastPage
- start Month cannot be after current Month
- current Month cannot be after end Month
- firstDayOfWeek is not valid
- There must be a keyline marked as focal.
AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14).
Data as JSON: /api/errors/a893ee9b23cc8537.
Report an issue: GitHub.