DrKLO/Telegram · critical · IllegalArgumentException
Called attach on a child which is not detached: {vh}
Error message
Called attach on a child which is not detached: {vh} What it means
RecyclerView's internal LayoutManager proxy implements attachViewToParent. Before attaching a child it checks the child's ViewHolder: if the holder is NOT marked tmp-detached and does not carry the shouldIgnore flag, it throws 'Called attach on a child which is not detached'. Attaching a view that the ViewGroup still considers attached would corrupt the view tree.
Source
Thrown at TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java:969
// detaching when being removed. If a child is re-added before the
// lazy detach occurs, it will receive invalid attach/detach sequencing.
child.clearAnimation();
}
RecyclerView.this.removeAllViews();
}
@Override
public ViewHolder getChildViewHolder(View view) {
return getChildViewHolderInt(view);
}
@Override
public void attachViewToParent(View child, int index,
ViewGroup.LayoutParams layoutParams) {
final ViewHolder vh = getChildViewHolderInt(child);
if (vh != null) {
if (!vh.isTmpDetached() && !vh.shouldIgnore()) {
throw new IllegalArgumentException("Called attach on a child which is not"
+ " detached: " + vh + exceptionLabel());
}
vh.clearTmpDetachFlag();
}
RecyclerView.this.attachViewToParent(child, index, layoutParams);
}
@Override
public void detachViewFromParent(int offset) {
final View view = getChildAt(offset);
if (view != null) {
final ViewHolder vh = getChildViewHolderInt(view);
if (vh != null) {
if (vh.isTmpDetached() && !vh.shouldIgnore()) {
throw new IllegalArgumentException("called detach on an already"
+ " detached child " + vh + exceptionLabel());
}
vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);View on GitHub (pinned to 45ab8f4308)
Solutions
- Ensure every attachViewToParent call is preceded by a matching detachViewFromParent (or removeView) for the same child.
- Use the Recycler API (recycler.getViewForPosition) instead of manual attach when possible.
- In onLayoutChildren, follow detach-all -> fill -> reattach pattern strictly; never attach a view you did not detach.
Example fix
// before (custom LM)
detachView(child); // forgot for some path
attachViewToParent(child, 0, lp);
// after
// only attach views you previously detached in this layout pass
if (isDetached(child)) {
attachViewToParent(child, 0, lp);
} Defensive patterns
Strategy: validation
Validate before calling
// In a custom LayoutManager, only attach children you have detached in this pass
View child = getChildAt(i);
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
if (vh != null && (vh.isTmpDetached() || vh.shouldIgnore())) {
attachViewToParent(child, index, lp);
} Type guard
null
Try / catch
null
Prevention
- Follow strict detach-then-attach ordering in onLayoutChildren.
- Prefer Recycler.getViewForPosition over manual attach when possible.
- Add assertions in debug builds that track attach/detach pairs.
When it happens
Trigger: A custom LayoutManager calls layoutManager.attachViewToParent / addView on a child that was never detached via the matching detach path. A mismatched attach/detach sequence in onLayoutChildren (e.g. attach called twice, or attach without a prior detachView).
Common situations: Hand-rolled LayoutManager that manages its own scrap recycling and forgets to detach before re-attaching. Off-by-one in recycler/scrap bookkeeping where a still-attached view is reattached. Recycler reuse race during predictive animations.
Related errors
- called detach on an already detached child {vh}
- LayoutManager {layout} is already attached to a RecyclerView
- view is not a child, cannot hide {}
- Layout positions must be non-negative
- Pixel distance must be non-negative
AI-assisted analysis of DrKLO/Telegram@45ab8f4308 (2026-08-14).
Data as JSON: /api/errors/150feb662663d84c.
Report an issue: GitHub.