{"record":{"id":"c3e49d232606a13f","repo":"DrKLO/Telegram","slug":"layout-positions-must-be-non-negative","errorCode":null,"errorMessage":"Layout positions must be non-negative","messagePattern":"Layout positions must be non-negative","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"TMessagesProj/src/main/java/androidx/recyclerview/widget/GapWorker.java","lineNumber":116,"sourceCode":"                    // momentum based prefetch, only if we trust current child/adapter state\n                    if (!view.hasPendingAdapterUpdates()) {\n                        layout.collectAdjacentPrefetchPositions(mPrefetchDx, mPrefetchDy,\n                                view.mState, this);\n                    }\n                }\n\n                if (mCount > layout.mPrefetchMaxCountObserved) {\n                    layout.mPrefetchMaxCountObserved = mCount;\n                    layout.mPrefetchMaxObservedInInitialPrefetch = nested;\n                    view.mRecycler.updateViewCacheSize();\n                }\n            }\n        }\n\n        @Override\n        public void addPosition(int layoutPosition, int pixelDistance) {\n            if (layoutPosition < 0) {\n                throw new IllegalArgumentException(\"Layout positions must be non-negative\");\n            }\n\n            if (pixelDistance < 0) {\n                throw new IllegalArgumentException(\"Pixel distance must be non-negative\");\n            }\n\n            // allocate or expand array as needed, doubling when needed\n            final int storagePosition = mCount * 2;\n            if (mPrefetchArray == null) {\n                mPrefetchArray = new int[4];\n                Arrays.fill(mPrefetchArray, -1);\n            } else if (storagePosition >= mPrefetchArray.length) {\n                final int[] oldArray = mPrefetchArray;\n                mPrefetchArray = new int[storagePosition * 2];\n                System.arraycopy(oldArray, 0, mPrefetchArray, 0, oldArray.length);\n            }\n\n            // add position","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/DrKLO/Telegram/blob/45ab8f4308496e1f01026a97fcdb0d58a5274474/TMessagesProj/src/main/java/androidx/recyclerview/widget/GapWorker.java#L98-L134","documentation":"GapWorker prefetches views likely to scroll into view soon. A nested LayoutManager reports positions it wants prefetched via LayoutManager.LayoutPrefetchRegistry.addPosition. A negative layoutPosition is meaningless (RecyclerView positions are 0-based and a negative index would index the wrong item or underflow), so addPosition rejects it. This is called from LayoutManager.onInitialPrefetch or nested RecyclerView prefetch; reaching it usually means a custom LayoutManager computed a position with arithmetic that underflowed (e.g. firstVisibleItem - 1 when firstVisibleItem is 0).","triggerScenarios":"A custom LayoutManager override of collectAdjacentPrefetchPositions or onInitialPrefetch that subtracts from a position without clamping; nested RecyclerView whose inner adapter has 0 items; prefetch logic that uses getChildAdapterPosition which returned NO_POSITION (-1).","commonSituations":"Custom carousel or staggered LayoutManager with hand-written prefetch; nested horizontal RecyclerView inside a vertical one where the inner list is empty; off-by-one in prefetch distance math after a data clear.","solutions":["In custom prefetch code, clamp positions to >= 0 before calling addPosition.","Skip the addPosition call entirely when getChildAdapterPosition returns RecyclerView.NO_POSITION.","Guard prefetch against empty adapters (getItemCount() == 0)."],"exampleFix":"// before\nint pos = getChildAdapterPosition(child) - 1;\nprefetchRegistry.addPosition(pos, distance); // pos may be -1\n\n// after\nint pos = getChildAdapterPosition(child) - 1;\nif (pos >= 0 && pos < getAdapter().getItemCount()) {\n    prefetchRegistry.addPosition(pos, distance);\n}","handlingStrategy":"validation","validationCode":"// Clamp prefetch positions to valid range\nvoid safeAddPosition(LayoutPrefetchRegistry reg, int pos, int dist) {\n    if (pos < 0 || pos >= getAdapter().getItemCount()) return;\n    reg.addPosition(pos, dist);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat RecyclerView.NO_POSITION (-1) from getChildAdapterPosition as 'skip prefetch'.","Clamp every computed prefetch position to >= 0 and < getItemCount.","Guard against empty adapters before prefetching."],"tags":["recyclerview","gapworker","prefetch","layoutmanager","nested"],"backgroundTag":null,"analyzedSha":"45ab8f4308496e1f01026a97fcdb0d58a5274474","analyzedAt":"2026-08-14T05:19:30.815Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}