{"record":{"id":"601c061be11702dd","repo":"material-components/material-components-android","slug":"invalid-shadow-bounds-check-that-the-treatments-r","errorCode":null,"errorMessage":"Invalid shadow bounds. Check that the treatments result in a valid path. extra width: %d extra height: %d path bounds: %s","messagePattern":"Invalid shadow bounds\\. Check that the treatments result in a valid path\\. extra width: (.+?) extra height: (.+?) path bounds: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"lib/java/com/google/android/material/shape/MaterialShapeDrawable.java","lineNumber":1185,"sourceCode":"      return;\n    }\n    // Save the canvas before changing the clip bounds.\n    canvas.save();\n    prepareCanvasForShadow(canvas);\n    if (!shadowBitmapDrawingEnable) {\n      drawCompatShadow(canvas);\n      canvas.restore();\n      return;\n    }\n\n    Rect drawableBounds = getBounds();\n    // The extra height is the amount that the path draws outside of the bounds of the shape. This\n    // happens for some shapes like TriangleEdgeTreatment when it draws a triangle outside.\n    int pathExtraWidth = (int) (pathBounds.width() - drawableBounds.width());\n    int pathExtraHeight = (int) (pathBounds.height() - drawableBounds.height());\n\n    if (pathExtraWidth < 0 || pathExtraHeight < 0) {\n      throw new IllegalStateException(\n          \"Invalid shadow bounds. Check that the treatments result in a valid path.\"\n              + \" extra width: \"\n              + pathExtraWidth\n              + \" extra height: \"\n              + pathExtraHeight\n              + \" path bounds: \"\n              + pathBounds);\n    }\n\n    // Drawing the shadow in a bitmap lets us use the clear paint rather than using clipPath to\n    // prevent drawing shadow under the shape. clipPath has problems :-/\n    Bitmap shadowLayer =\n        Bitmap.createBitmap(\n            (int) pathBounds.width() + drawableState.shadowCompatRadius * 2 + pathExtraWidth,\n            (int) pathBounds.height() + drawableState.shadowCompatRadius * 2 + pathExtraHeight,\n            Bitmap.Config.ARGB_8888);\n    Canvas shadowCanvas = new Canvas(shadowLayer);\n","sourceCodeStart":1167,"sourceCodeEnd":1203,"githubUrl":"https://github.com/material-components/material-components-android/blob/ac7e18efeefb331850c561faf9ab8bf81d27ba68/lib/java/com/google/android/material/shape/MaterialShapeDrawable.java#L1167-L1203","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nclass InvertedTriangleEdge(size: Float) : EdgeTreatment(size) {\n    override fun getEdgePath(length: Float, center: Float, interpolation: Float, shapePath: ShapePath) {\n        shapePath.lineTo(center, -size) // wrong direction pulls path inside bounds\n    }\n}\n\n// after\nclass TriangleEdge(size: Float) : EdgeTreatment(size) {\n    override fun getEdgePath(length: Float, center: Float, interpolation: Float, shapePath: ShapePath) {\n        shapePath.lineTo(center, size) // extends outside drawable bounds as expected\n    }\n}","handlingStrategy":"try-catch","validationCode":"Rect bounds = drawable.getBounds();\nRectF pathBounds = new RectF();\ndrawable.getShapeAppearanceModel().toPath(false, pathBounds);\n// ensure path covers bounds before enabling elevation\nif (pathBounds.width() >= bounds.width() && pathBounds.height() >= bounds.height()) {\n  view.setElevation(elevationPx);\n}","typeGuard":null,"tryCatchPattern":"try { drawable.draw(canvas); } catch (IllegalStateException e) { Log.e(TAG, \"Invalid shape path for shadow; check EdgeTreatment math\", e); view.setElevation(0f); }","preventionTips":["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."],"tags":["android","material-components","shape","shadow","custom-view"],"backgroundTag":null,"analyzedSha":"ac7e18efeefb331850c561faf9ab8bf81d27ba68","analyzedAt":"2026-08-14T15:35:35.735Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}