{"record":{"id":"0c6daea05903469d","repo":"DrKLO/Telegram","slug":"holder-at-i-holder","errorCode":null,"errorMessage":"Holder at{i} {holder}\n...","messagePattern":"Holder at(.+?) (.+?)\n\\.\\.\\.","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java","lineNumber":4210,"sourceCode":"                            } else {\n                                animateChange(oldChangeViewHolder, holder, preInfo, postInfo,\n                                        oldDisappearing, newDisappearing);\n                            }\n                        }\n                    } else {\n                        mViewInfoStore.addToPostLayout(holder, animationInfo);\n                    }\n                }\n            } catch (Exception e) {\n                StringBuilder builder = new StringBuilder();\n                for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {\n                    ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));\n                    if (holder == null || holder.shouldIgnore()) {\n                        continue;\n                    }\n                    builder.append(\"Holder at\" + i + \" \" + holder + \"\\n\");\n                }\n                throw new RuntimeException(builder.toString(), e);\n            }\n\n            // Step 4: Process view info lists and trigger animations\n            mViewInfoStore.process(mViewInfoProcessCallback);\n        }\n\n        mLayout.removeAndRecycleScrapInt(mRecycler);\n        mState.mPreviousLayoutItemCount = mState.mItemCount;\n        mDataSetHasChangedAfterLayout = false;\n        mDispatchItemsChangedEvent = false;\n        mState.mRunSimpleAnimations = false;\n\n        mState.mRunPredictiveAnimations = false;\n        mLayout.mRequestedSimpleAnimations = false;\n        if (mRecycler.mChangedScrap != null) {\n            mRecycler.mChangedScrap.clear();\n        }\n        if (mLayout.mPrefetchMaxObservedInInitialPrefetch) {","sourceCodeStart":4192,"sourceCodeEnd":4228,"githubUrl":"https://github.com/DrKLO/Telegram/blob/45ab8f4308496e1f01026a97fcdb0d58a5274474/TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java#L4192-L4228","documentation":"In dispatchLayoutStep3 (the animation phase of layout), RecyclerView wraps the entire change-matching loop in try/catch. If ANY exception is thrown while recording post-layout info or matching changed ViewHolders, it rethrows a RuntimeException whose message is a dump of every attached, non-ignored ViewHolder with its index. This is a diagnostic wrapper: the real failure is the chained cause (Throwable e), not the holder list itself. The list is provided so the developer can inspect the live view-holder state at the moment of failure.","triggerScenarios":"An exception thrown inside ItemAnimator.recordPostLayoutInformation, animateChange, or animateMove/Remove during Step 3; or a downstream failure in mViewInfoStore operations. Typically caused by a custom ItemAnimator that throws, by the same duplicate-key/invalid-position defects (see errors 42/43/49) surfacing during animation, or by a ViewHolder being modified concurrently.","commonSituations":"Custom ItemAnimator with a null/buggy implementation; concurrent adapter mutation from a background thread that invalidates holders mid-animation; an ItemAnimator touching a recycled view; a prior inconsistent notify sequence that only surfaces corruption during Step 3.","solutions":["Inspect the chained cause (RuntimeException.getCause()) — that is the real failure; the holder dump is only context. Fix the underlying defect the cause names (duplicate stable ID, invalid position, NPE in custom animator).","If the cause points to a custom ItemAnimator, audit its recordPostLayoutInformation/animateChange implementations for null checks and for touching holders that may have been recycled.","Ensure all adapter mutations happen on the main thread and are never concurrent with a layout pass; route mutations through a single-threaded queue or DiffUtil.","Temporarily disable item animations (recyclerView.setItemAnimator(null)) to confirm whether the crash is animation-path-only; if it disappears, the ItemAnimator or an inconsistent-change notification is the culprit."],"exampleFix":"// before: a custom ItemAnimator throwing during change animation\n@Override public boolean animateChange(@NonNull RecyclerView.ViewHolder oldVh,\n    @NonNull RecyclerView.ViewHolder newVh,\n    @NonNull ItemHolderInfo preInfo, @NonNull ItemHolderInfo postInfo) {\n  return animateMove(newVh, preInfo.left, preInfo.top, postInfo.left, postInfo.top); // NPE if preInfo null\n}\n// after: guard against null info and fall back to a no-op change\n@Override public boolean animateChange(@NonNull RecyclerView.ViewHolder oldVh,\n    @NonNull RecyclerView.ViewHolder newVh,\n    @Nullable ItemHolderInfo preInfo, @Nullable ItemHolderInfo postInfo) {\n  if (preInfo == null || postInfo == null) {\n    dispatchChangeFinished(newVh, false);\n    return false;\n  }\n  return animateMove(newVh, preInfo.left, preInfo.top, postInfo.left, postInfo.top);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"// This is a framework-thrown wrapper; catch only at a top-level uncaught-exception handler\n// to report diagnostics, then FIX the chained cause rather than suppress it.\nThread.setDefaultUncaughtExceptionHandler((t, e) -> {\n  if (e instanceof RuntimeException && e.getMessage() != null\n      && e.getMessage().startsWith(\"Holder at\")) {\n    reportToCrashAnalytics(e.getCause() != null ? e.getCause() : e);\n  }\n});","preventionTips":["Treat this as a symptom, not the bug — always read RuntimeException.getCause().","Keep item animations off critical data-change paths until the underlying inconsistency is fixed.","Stress-test adapter updates (rapid insert/remove) under animations to surface latent inconsistencies early."],"tags":["recyclerview","animation","itemanimator","diagnostics"],"backgroundTag":null,"analyzedSha":"45ab8f4308496e1f01026a97fcdb0d58a5274474","analyzedAt":"2026-08-14T05:19:30.815Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}