material-components/material-components-android · error · IllegalArgumentException
Keylines that are marked as focal must all have the same mas
Error message
Keylines that are marked as focal must all have the same masked item size.
What it means
Every focal keyline in a KeylineState must share the same masked item size (KeylineState.java:409): when a focal keyline's maskedItemSize differs from the first focal keyline's (tmpFirstFocalKeyline.maskedItemSize), the builder throws. Focal items are the unmasked, full-size items; a focal run with mixed sizes would make 'focal' meaningless for scroll-snap and focal-range calculations.
Source
Thrown at lib/java/com/google/android/material/carousel/KeylineState.java:409
latestAnchorKeylineIndex = tmpKeylines.size();
}
Keyline tmpKeyline =
new Keyline(UNKNOWN_LOC, offsetLoc, mask, maskedItemSize, isAnchor, cutoff,
leftOrTopPaddingShift, rightOrBottomPaddingShift);
if (isFocal) {
if (tmpFirstFocalKeyline == null) {
tmpFirstFocalKeyline = tmpKeyline;
firstFocalKeylineIndex = tmpKeylines.size();
}
if (lastFocalKeylineIndex != NO_INDEX && tmpKeylines.size() - lastFocalKeylineIndex > 1) {
throw new IllegalArgumentException(
"Keylines marked as focal must be placed next to each other. There cannot be"
+ " non-focal keylines between focal keylines.");
}
if (maskedItemSize != tmpFirstFocalKeyline.maskedItemSize) {
throw new IllegalArgumentException(
"Keylines that are marked as focal must all have the same masked item size.");
}
tmpLastFocalKeyline = tmpKeyline;
lastFocalKeylineIndex = tmpKeylines.size();
} else {
if (tmpFirstFocalKeyline == null && tmpKeyline.maskedItemSize < lastKeylineMaskedSize) {
throw new IllegalArgumentException(
"Keylines before the first focal keyline must be ordered by incrementing masked item"
+ " size.");
} else if (tmpLastFocalKeyline != null
&& tmpKeyline.maskedItemSize > lastKeylineMaskedSize) {
throw new IllegalArgumentException(
"Keylines after the last focal keyline must be ordered by decreasing masked item"
+ " size.");
}
}
lastKeylineMaskedSize = tmpKeyline.maskedItemSize;
tmpKeylines.add(tmpKeyline);View on GitHub (pinned to ac7e18efee)
Solutions
- Flag exactly one masked size as focal; keylines with other sizes must be non-focal.
- If sizes are computed, derive them from the same itemSize constant so float equality holds (maskedItemSize for focal keylines should equal the unmasked item size).
- Check a hero-style strategy (HeroCarouselStrategy) for the correct pattern when you need varied center sizes.
Example fix
// before builder.addKeyline(o1, 0f, itemSize, true, false); builder.addKeyline(o2, 0f, itemSize * 0.8f, true, false); // throws: focal sizes differ // after builder.addKeyline(o1, 0f, itemSize, true, false); builder.addKeyline(o2, 0.2f, itemSize * 0.8f, false, false); // non-focal, different size OK
Defensive patterns
Strategy: validation
Validate before calling
boolean focal = Math.abs(maskedItemSize - itemSize) < 0.01f; // single focal size only builder.addKeyline(offset, mask, maskedItemSize, focal, anchor);
Prevention
- Only one masked size may be focal — derive all focal sizes from the same itemSize constant.
- In hero-style layouts, flag only the large size as focal.
- Compute sizes from one source constant to avoid float-equality surprises.
When it happens
Trigger: Custom CarouselStrategy marking keylines with different masked sizes (e.g. a large 300dp item and a medium 240dp item) as isFocal=true in the same builder. Any per-index size variation combined with isFocal=true triggers it as soon as the second, differently-sized focal keyline is added.
Common situations: Strategies that vary item size across the center of the carousel (hero-style layouts) but forget that only one size may be focal — in HeroCarouselStrategy-like layouts the large item is the focal and other sizes must not be flagged focal. Arithmetic bugs producing slightly different masked sizes (float rounding on different offsets) can also cause inequality.
Related errors
- Anchor keylines cannot be focal.
- Anchor keylines must be either the first or last keyline.
- Keylines marked as focal must be placed next to each other.
- Keylines before the first focal keyline must be ordered by i
- Keylines after the last focal keyline must be ordered by dec
AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14).
Data as JSON: /api/errors/0065100118a073b1.
Report an issue: GitHub.