{"record":{"id":"45e88a33c93847a3","repo":"DrKLO/Telegram","slug":"called-detach-on-an-already-detached-child-vh","errorCode":null,"errorMessage":"called detach on an already detached child {vh}","messagePattern":"called detach on an already detached child (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"critical","filePath":"TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java","lineNumber":984,"sourceCode":"                final ViewHolder vh = getChildViewHolderInt(child);\n                if (vh != null) {\n                    if (!vh.isTmpDetached() && !vh.shouldIgnore()) {\n                        throw new IllegalArgumentException(\"Called attach on a child which is not\"\n                                + \" detached: \" + vh + exceptionLabel());\n                    }\n                    vh.clearTmpDetachFlag();\n                }\n                RecyclerView.this.attachViewToParent(child, index, layoutParams);\n            }\n\n            @Override\n            public void detachViewFromParent(int offset) {\n                final View view = getChildAt(offset);\n                if (view != null) {\n                    final ViewHolder vh = getChildViewHolderInt(view);\n                    if (vh != null) {\n                        if (vh.isTmpDetached() && !vh.shouldIgnore()) {\n                            throw new IllegalArgumentException(\"called detach on an already\"\n                                    + \" detached child \" + vh + exceptionLabel());\n                        }\n                        vh.addFlags(ViewHolder.FLAG_TMP_DETACHED);\n                    }\n                }\n                RecyclerView.this.detachViewFromParent(offset);\n            }\n\n            @Override\n            public void onEnteredHiddenState(View child) {\n                final ViewHolder vh = getChildViewHolderInt(child);\n                if (vh != null) {\n                    vh.onEnteredHiddenState(RecyclerView.this);\n                }\n            }\n\n            @Override\n            public void onLeftHiddenState(View child) {","sourceCodeStart":966,"sourceCodeEnd":1002,"githubUrl":"https://github.com/DrKLO/Telegram/blob/45ab8f4308496e1f01026a97fcdb0d58a5274474/TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java#L966-L1002","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom LayoutManager with faulty bookkeeping during onLayoutChildren. Misuse of detachAndScrapView vs detachViewFromParent. Predictive animation path that detaches a view the layout already detached.","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."],"exampleFix":"// before\nlayoutManager.detachViewAt(i); // may already be detached\n\n// after\nViewHolder vh = recyclerView.getChildViewHolder(child);\nif (vh == null || !vh.isTmpDetached()) {\n    layoutManager.detachViewAt(i);\n}","handlingStrategy":"validation","validationCode":"View child = getChildAt(i);\nRecyclerView.ViewHolder vh = recyclerView.getChildViewHolder(child);\nif (vh == null || !vh.isTmpDetached()) {\n    detachViewFromParent(i);\n}","typeGuard":"null","tryCatchPattern":"null","preventionTips":["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."],"tags":["recyclerview","layoutmanager","view-tree","lifecycle"],"backgroundTag":null,"analyzedSha":"45ab8f4308496e1f01026a97fcdb0d58a5274474","analyzedAt":"2026-08-14T05:19:30.815Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}