material-components/material-components-android · error · IllegalArgumentException
Keylines before the first focal keyline must be ordered by i
Error message
Keylines before the first focal keyline must be ordered by incrementing masked item size.
What it means
Before the first focal keyline, keylines must grow monotonically in masked size as they approach the focal run (KeylineState.java:416). The builder tracks lastKeylineMaskedSize; when no focal keyline has been added yet (tmpFirstFocalKeyline == null) and the new non-focal keyline's maskedItemSize is smaller than the previous one, it throws. This ordering is what produces the carousel's characteristic 'items grow toward the center' masking curve.
Source
Thrown at lib/java/com/google/android/material/carousel/KeylineState.java:416
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);
return this;
}
/**
* Adds a keyline along the scrolling axis where an object should be masked by the given {@code
* mask} and positioned at {@code offsetLoc}.
*View on GitHub (pinned to ac7e18efee)
Solutions
- Order leading keylines ascending in maskedItemSize (smallest first, each >= the previous) up to the focal size.
- If sizes come from an array/list, sort or reverse it so it ascends before the focal run.
- Add a debug assertion in the strategy that logs each pre-focal size and fails fast on non-increasing sequences.
Example fix
// before builder.addKeyline(o0, 0.9f, size*0.3f, false, false); builder.addKeyline(o1, 0.7f, size*0.2f, false, false); // throws: decreased builder.addKeyline(o2, 0.5f, size*0.6f, false, false); // after builder.addKeyline(o0, 0.9f, size*0.2f, false, false); builder.addKeyline(o1, 0.7f, size*0.3f, false, false); builder.addKeyline(o2, 0.5f, size*0.6f, false, false);
Defensive patterns
Strategy: validation
Validate before calling
double prevSize = 0;
for (KeylineSpec k : leadingKeylines) {
if (k.maskedItemSize <= prevSize) throw new IllegalStateException("leading sizes must ascend");
prevSize = k.maskedItemSize;
builder.addKeyline(k.offset, k.mask, k.maskedItemSize, false, false);
} Prevention
- Emit leading keylines in ascending masked size.
- Sort or reverse size arrays when flipping orientation/direction.
- Add strategy unit tests asserting monotonic ordering.
When it happens
Trigger: Custom CarouselStrategy emitting leading keylines in decreasing or jumbled size order — e.g. sizes [0.9, 0.5, 0.7] before the first focal keyline. The check applies only to keylines added before any isFocal=true keyline, and only for positive maskedItemSize (size <= 0F entries are skipped earlier).
Common situations: Loop-generated keylines whose size formula decreases with index when it should increase, or shuffled/copy-pasted keyline arrays. Also migrating a strategy between horizontal and vertical and flipping the iteration direction without flipping the size order.
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 that are marked as focal must all have the same mas
- 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/12d60d36bb81c684.
Report an issue: GitHub.