material-components/material-components-android · error · IllegalArgumentException
> resourceName < is not a valid ancestor
Error message
> resourceName < is not a valid ancestor
What it means
TransitionUtils.findAncestorById walks up from a given view's parent chain looking for a view whose id equals ancestorId; if the root is reached without a match it throws IllegalArgumentException(resourceName + ' is not a valid ancestor'). MaterialContainerTransform calls this (MaterialContainerTransform.java:920) with its drawingViewId to locate the view whose overlay the transform should draw in — so the drawing view MUST be an ancestor of the transitioning start/end view in the current hierarchy.
Source
Thrown at lib/java/com/google/android/material/transition/platform/TransitionUtils.java:290
return descendant;
}
return findAncestorById(view, viewId);
}
static View findAncestorById(View view, @IdRes int ancestorId) {
String resourceName = view.getResources().getResourceName(ancestorId);
while (view != null) {
if (view.getId() == ancestorId) {
return view;
}
ViewParent parent = view.getParent();
if (parent instanceof View) {
view = (View) parent;
} else {
break;
}
}
throw new IllegalArgumentException(resourceName + " is not a valid ancestor");
}
static RectF getRelativeBounds(View view) {
return new RectF(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
}
static Rect getRelativeBoundsRect(View view) {
return new Rect(view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
}
static RectF getLocationInWindow(View view) {
int[] location = new int[2];
view.getLocationInWindow(location);
int left = location[0];
int top = location[1];
int right = left + view.getWidth();
int bottom = top + view.getHeight();
return new RectF(left, top, right, bottom);View on GitHub (pinned to ac7e18efee)
Solutions
- Ensure drawingViewId points to a real ancestor: use the id of the parent layout that contains BOTH start and end views, or android.R.id.content only when the views live in the activity content view.
- For DialogFragment-based transforms, set the drawing view to the dialog's decor/content container (or the dialog's own root id), not the activity's content id.
- Cancel or defer the transition if the start/end view gets detached (guard with a TransitionListener and lifecycle checks) so findAncestor never runs against a stale hierarchy.
- Verify ids are unique across the inflated layouts — a duplicate id on a non-ancestor can mask the real ancestor during the walk.
Example fix
// before MaterialContainerTransform transform = new MaterialContainerTransform(); transform.setDrawingViewId(R.id.detail_card); // sibling, not ancestor -> throws // after MaterialContainerTransform transform = new MaterialContainerTransform(); transform.setDrawingViewId(R.id.root_content); // ancestor of both start and end views
Defensive patterns
Strategy: validation
Validate before calling
// Walk up from the shared element and confirm drawingViewId is on its parent
// chain BEFORE scheduling the container transform.
static boolean isAncestorById(View view, @IdRes int ancestorId) {
for (View v = view; v != null; ) {
if (v.getId() == ancestorId) return true;
ViewParent p = v.getParent();
v = (p instanceof View) ? (View) p : null;
}
return false;
}
// usage
if (isAncestorById(sharedElement, transform.getDrawingViewId())) {
TransitionManager.beginDelayedTransition(root, transform);
} else {
transform.setDrawingViewId(android.R.id.content); // known ancestor fallback
} Try / catch
try {
fragment.setSharedElementEnterTransition(transform);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().endsWith("is not a valid ancestor")) {
transform.setDrawingViewId(android.R.id.content);
fragment.setSharedElementEnterTransition(transform); // retry with real ancestor
} else {
throw e;
}
} Prevention
- Choose drawingViewId as a layout container that physically contains BOTH start and end views.
- For dialogs, use the dialog's own content container id, not the activity's content id.
- Cancel scheduled transforms in onDestroyView so they never run against a rebuilt hierarchy.
- Keep view ids unique across navigation graph layouts.
When it happens
Trigger: MaterialContainerTransform.setDrawingViewId(id) set to an id that is a sibling, a descendant, or a view in a different window/Fragment hierarchy than the transitioned view; using android.R.id.content or a container id that is not on the parent chain of the shared element; view hierarchy rebuilt between scheduling and running the transition (e.g., RecyclerView recycle or Fragment view destroyed), so the id no longer sits above the view.
Common situations: Container transform shared-element transitions where drawingViewId defaults to android.R.id.content but the transitioned view lives in a DialogFragment or different window; setting the drawing view to the Fragment's inner container while the shared element is in a parent layout; running the transition after the view was detached and re-attached elsewhere.
Related errors
- > resourceName < is not a valid ancestor
- MaterialSplitButton can only hold MaterialButtons.
- MaterialSplitButton can only hold two MaterialButtons.
- We already have an EditText, can only have one
- The view is not a child of CoordinatorLayout
AI-assisted analysis of material-components/material-components-android@ac7e18efee (2026-08-14).
Data as JSON: /api/errors/38543f52f3dff0e1.
Report an issue: GitHub.