DrKLO/Telegram · critical · IllegalArgumentException
called detach on an already detached child {vh}
Error message
called detach on an already detached child {vh} What it means
The symmetric guard in detachViewFromParent: if the child's ViewHolder is ALREADY tmp-detached and is not shouldIgnore, RecyclerView throws 'called detach on an already detached child'. Detaching twice leaves the detach bookkeeping inconsistent and would orphan the view from the layout pass.
Source
Thrown at TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java:984
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);
}
}
RecyclerView.this.detachViewFromParent(offset);
}
@Override
public void onEnteredHiddenState(View child) {
final ViewHolder vh = getChildViewHolderInt(child);
if (vh != null) {
vh.onEnteredHiddenState(RecyclerView.this);
}
}
@Override
public void onLeftHiddenState(View child) {View on GitHub (pinned to 45ab8f4308)
Solutions
- Track detached state explicitly and never call detach twice without an attach in between.
- Prefer recycler.recycleView / detachAndScrapView over raw detachViewFromParent.
- Guard each detach with a check against the child's current state.
Example fix
// before
layoutManager.detachViewAt(i); // may already be detached
// after
ViewHolder vh = recyclerView.getChildViewHolder(child);
if (vh == null || !vh.isTmpDetached()) {
layoutManager.detachViewAt(i);
} Defensive patterns
Strategy: validation
Validate before calling
View child = getChildAt(i);
RecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);
if (vh == null || !vh.isTmpDetached()) {
detachViewFromParent(i);
} Type guard
null
Try / catch
null
Prevention
- Maintain explicit detach bookkeeping; never detach twice without an attach between.
- Prefer recycler.recycleView / detachAndScrapView over raw detachViewFromParent.
- Audit custom LayoutManager detach calls against attach calls during code review.
When it happens
Trigger: A custom LayoutManager calls detachViewFromParent twice on the same child without an intervening attach. A scrap/recycle routine that detaches a view already held in a detached scrap.
Common situations: Custom LayoutManager with faulty bookkeeping during onLayoutChildren. Misuse of detachAndScrapView vs detachViewFromParent. Predictive animation path that detaches a view the layout already detached.
Related errors
- Called attach on a child which is not detached: {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/45e88a33c93847a3.
Report an issue: GitHub.