material-components/material-components-android · error · IllegalStateException
Invalid shadow bounds. Check that the treatments result in a
Error message
Invalid shadow bounds. Check that the treatments result in a valid path. extra width: %d extra height: %d path bounds: %s
What it means
MaterialShapeDrawable's shadow-casting path throws IllegalStateException when the shape's path bounds are smaller than the drawable bounds in width or height (negative extra). Edge treatments like TriangleEdgeTreatment can legitimately draw outside bounds, but a path narrower/shorter than the drawable means the treatments produced an invalid path, and shadow rendering would be broken, so it fails fast with the measured extras and path bounds in the message.
Source
Thrown at lib/java/com/google/android/material/shape/MaterialShapeDrawable.java:1185
return;
}
// Save the canvas before changing the clip bounds.
canvas.save();
prepareCanvasForShadow(canvas);
if (!shadowBitmapDrawingEnable) {
drawCompatShadow(canvas);
canvas.restore();
return;
}
Rect drawableBounds = getBounds();
// The extra height is the amount that the path draws outside of the bounds of the shape. This
// happens for some shapes like TriangleEdgeTreatment when it draws a triangle outside.
int pathExtraWidth = (int) (pathBounds.width() - drawableBounds.width());
int pathExtraHeight = (int) (pathBounds.height() - drawableBounds.height());
if (pathExtraWidth < 0 || pathExtraHeight < 0) {
throw new IllegalStateException(
"Invalid shadow bounds. Check that the treatments result in a valid path."
+ " extra width: "
+ pathExtraWidth
+ " extra height: "
+ pathExtraHeight
+ " path bounds: "
+ pathBounds);
}
// Drawing the shadow in a bitmap lets us use the clear paint rather than using clipPath to
// prevent drawing shadow under the shape. clipPath has problems :-/
Bitmap shadowLayer =
Bitmap.createBitmap(
(int) pathBounds.width() + drawableState.shadowCompatRadius * 2 + pathExtraWidth,
(int) pathBounds.height() + drawableState.shadowCompatRadius * 2 + pathExtraHeight,
Bitmap.Config.ARGB_8888);
Canvas shadowCanvas = new Canvas(shadowLayer);
View on GitHub (pinned to ac7e18efee)
Solutions
- Check the EdgeTreatment math: edge intersections should extend outside the canvas bounds; verify getEdgePath() offset signs and sizes.
- Verify the ShapeAppearanceModel corner sizes fit the view dimensions (corner size should not exceed half the smaller side).
- Temporarily disable elevation/shadow to isolate, and log the drawable bounds vs path bounds to find which treatment shrinks the path.
- Use ShapeAppearanceModel builder defaults and re-add treatments one at a time until the offender is found.
Example fix
// before
class InvertedTriangleEdge(size: Float) : EdgeTreatment(size) {
override fun getEdgePath(length: Float, center: Float, interpolation: Float, shapePath: ShapePath) {
shapePath.lineTo(center, -size) // wrong direction pulls path inside bounds
}
}
// after
class TriangleEdge(size: Float) : EdgeTreatment(size) {
override fun getEdgePath(length: Float, center: Float, interpolation: Float, shapePath: ShapePath) {
shapePath.lineTo(center, size) // extends outside drawable bounds as expected
}
} Defensive patterns
Strategy: try-catch
Validate before calling
Rect bounds = drawable.getBounds();
RectF pathBounds = new RectF();
drawable.getShapeAppearanceModel().toPath(false, pathBounds);
// ensure path covers bounds before enabling elevation
if (pathBounds.width() >= bounds.width() && pathBounds.height() >= bounds.height()) {
view.setElevation(elevationPx);
} Try / catch
try { drawable.draw(canvas); } catch (IllegalStateException e) { Log.e(TAG, "Invalid shape path for shadow; check EdgeTreatment math", e); view.setElevation(0f); } Prevention
- Unit-test custom EdgeTreatments by asserting the produced path is >= drawable bounds.
- Prefer the built-in edge treatments unless you fully control path direction.
- Test on all API levels you support, since shadow rendering paths differ below API 21.
When it happens
Trigger: Creating a MaterialShapeDrawable with an EdgeTreatment or ShapeAppearanceModel whose generated path is degenerate or smaller than the view — e.g. a triangle edge with 0 size on a shape whose corners invert the path, or a custom EdgeTreatment that moves points inside instead of outside.
Common situations: Custom EdgeTreatment implementations with wrong sign on offsets; using TriangleEdgeTreatment/OffsetEdgeTreatment with sizes that collapse the path; setting shapeAppearanceModel with mismatched corner sizes relative to view dimensions; shadow elevation > 0 exposing the path only when shadows are drawn (API < 21 or compat shadow path).
Related errors
- Attempted to set ShapeAppearanceModel on a MaterialButton wh
- Attempted to get ShapeAppearanceModel from a MaterialButton
- Attempted to set ShapeAppearance on a MaterialButton which h
- Attempted to get ShapeAppearance from a MaterialButton which
- invalid shadow size
AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14).
Data as JSON: /api/errors/601c061be11702dd.
Report an issue: GitHub.