{"record":{"id":"4e022f2ee23a672c","repo":"DrKLO/Telegram","slug":"invalid-position-position-state-item-count-is","errorCode":null,"errorMessage":"invalid position {position}. State item count is {itemCount}","messagePattern":"invalid position (.+?)\\. State item count is (.+?)","errorType":"exception","errorClass":"IndexOutOfBoundsException","httpStatus":null,"severity":"error","filePath":"TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java","lineNumber":6107,"sourceCode":"         * automatically maps these positions to {@link Adapter} positions when\n         * {@link #getViewForPosition(int)} or {@link #bindViewToPosition(View, int)} is called.\n         * <p>\n         * Usually, LayoutManager does not need to worry about this. However, in some cases, your\n         * LayoutManager may need to call some custom component with item positions in which\n         * case you need the actual adapter position instead of the pre layout position. You\n         * can use this method to convert a pre-layout position to adapter (post layout) position.\n         * <p>\n         * Note that if the provided position belongs to a deleted ViewHolder, this method will\n         * return -1.\n         * <p>\n         * Calling this method in post-layout state returns the same value back.\n         *\n         * @param position The pre-layout position to convert. Must be greater or equal to 0 and\n         *                 less than {@link State#getItemCount()}.\n         */\n        public int convertPreLayoutPositionToPostLayout(int position) {\n            if (position < 0 || position >= mState.getItemCount()) {\n                throw new IndexOutOfBoundsException(\"invalid position \" + position + \". State \"\n                        + \"item count is \" + mState.getItemCount() + exceptionLabel());\n            }\n            if (!mState.isPreLayout()) {\n                return position;\n            }\n            return mAdapterHelper.findPositionOffset(position);\n        }\n\n        /**\n         * Obtain a view initialized for the given position.\n         *\n         * This method should be used by {@link LayoutManager} implementations to obtain\n         * views to represent data from an {@link Adapter}.\n         * <p>\n         * The Recycler may reuse a scrap or detached view from a shared pool if one is\n         * available for the correct view type. If the adapter has not indicated that the\n         * data at the given position has changed, the Recycler will attempt to hand back\n         * a scrap view that was previously initialized for that data without rebinding.","sourceCodeStart":6089,"sourceCodeEnd":6125,"githubUrl":"https://github.com/DrKLO/Telegram/blob/45ab8f4308496e1f01026a97fcdb0d58a5274474/TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java#L6089-L6125","documentation":"Recycler.convertPreLayoutPositionToPostLayout throws IndexOutOfBoundsException ('invalid position') when the supplied position is outside [0, mState.getItemCount()). This method maps a pre-layout position to its post-layout adapter position; it requires the input to be a valid position in the current State's item count (which during pre-layout includes items pending removal). Passing a negative or >= itemCount position violates the precondition before the offset translation is even attempted.","triggerScenarios":"A LayoutManager computing pre-layout positions passing a stale or out-of-range index; calling this outside a layout pass with a position derived from a previous, now-invalid state; off-by-one where a position equal to getItemCount() is passed (must be < count).","commonSituations":"Custom LayoutManager caching positions across state resets; calling convertPreLayoutPositionToPostLayout during post-layout (where it returns the position unchanged but still validates bounds); adapter count shrunk between position capture and conversion call.","solutions":["Clamp/guard the input position: ensure 0 <= position < state.getItemCount() before calling; re-read count from the current State each time.","Do not cache positions across layout passes in a custom LayoutManager — derive them fresh from State each layout.","Ensure the call is only made during pre-layout (State.isPreLayout()); in post-layout the conversion is the identity and the bounds check still applies."],"exampleFix":"// before (custom LayoutManager)\nint postPos = recycler.convertPreLayoutPositionToPostLayout(cachedPos); // cachedPos stale\n// after\nint count = state.getItemCount();\nif (cachedPos >= 0 && cachedPos < count) {\n  int postPos = recycler.convertPreLayoutPositionToPostLayout(cachedPos);\n} else {\n  // position no longer valid; skip or recompute\n}","handlingStrategy":"validation","validationCode":"int count = state.getItemCount();\nif (position >= 0 && position < count) {\n  int post = recycler.convertPreLayoutPositionToPostLayout(position);\n} else {\n  // invalid; skip or recompute\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In custom LayoutManagers, derive positions fresh from State each pass.","Only call convertPreLayoutPositionToPostLayout during pre-layout (State.isPreLayout()).","Never cache positions across state resets."],"tags":["recyclerview","layoutmanager","pre-layout","position-tracking"],"backgroundTag":null,"analyzedSha":"45ab8f4308496e1f01026a97fcdb0d58a5274474","analyzedAt":"2026-08-14T05:19:30.815Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}