DrKLO/Telegram · error · IndexOutOfBoundsException

Index out of bounds - passed position = {}, new list size =

Error message

Index out of bounds - passed position = {}, new list size = {}

What it means

The mirror of error 9: convertNewPositionToOld maps a position in the NEW list back to the old list. mNewItemStatuses was sized to mNewListSize at DiffResult construction. Passing newListPosition outside [0, mNewListSize) is out of contract and would index out of bounds, so DiffUtil throws. This usually means the new list shrank or the caller is using a stale/newer list's position against an older DiffResult.

Source

Thrown at TMessagesProj/src/main/java/androidx/recyclerview/widget/DiffUtil.java:696

            } else {
                return status >> FLAG_OFFSET;
            }
        }

        /**
         * Given a position in the new list, returns the position in the old list, or
         * {@code NO_POSITION} if it was removed.
         *
         * @param newListPosition Position of item in new list
         *
         * @return Position of item in old list, or {@code NO_POSITION} if not present.
         *
         * @see #NO_POSITION
         * @see #convertOldPositionToNew(int)
         */
        public int convertNewPositionToOld(@IntRange(from = 0) int newListPosition) {
            if (newListPosition < 0 || newListPosition >= mNewListSize) {
                throw new IndexOutOfBoundsException("Index out of bounds - passed position = "
                        + newListPosition + ", new list size = " + mNewListSize);
            }
            final int status = mNewItemStatuses[newListPosition];
            if ((status & FLAG_MASK) == 0) {
                return NO_POSITION;
            } else {
                return status >> FLAG_OFFSET;
            }
        }

        /**
         * Finds a matching item that is before the given coordinates in the matrix
         * (before : left and above).
         *
         * @param x The x position in the matrix (position in the old list)
         * @param y The y position in the matrix (position in the new list)
         * @param snakeIndex The current snake index
         * @param removal True if we are looking for a removal, false otherwise

View on GitHub (pinned to 45ab8f4308)

Solutions

  1. Use positions strictly within [0, new list size) as captured at DiffResult creation.
  2. Recompute the diff on every data swap; never reuse a DiffResult after the backing list changes.
  3. Bounds-check against the size recorded at diff time before converting.

Example fix

// before
int oldPos = result.convertNewPositionToOld(newPos); // may be stale

// after
int newSize = newList.size();
int oldPos = (newPos >= 0 && newPos < newSize)
    ? result.convertNewPositionToOld(newPos)
    : DiffUtil.DiffResult.NO_POSITION;
Defensive patterns

Strategy: validation

Validate before calling

// Bounds-check against the new list size captured at diff time
int newSize = newSnap.size();
int safeConvertOld(DiffUtil.DiffResult r, int newPos) {
    if (newPos < 0 || newPos >= newSize) return DiffUtil.DiffResult.NO_POSITION;
    return r.convertNewPositionToOld(newPos);
}

Prevention

When it happens

Trigger: Querying convertNewPositionToOld with a position from a later list version after the new list was replaced; using the old list size as the upper bound by mistake; the new list was mutated post-diff (same root cause as error 8).

Common situations: Stale DiffResult reused after submitList; position arithmetic mixing old/new indices; adapter that reuses DiffResult across refresh cycles.

Related errors


AI-assisted analysis of DrKLO/Telegram@45ab8f4308 (2026-08-14). Data as JSON: /api/errors/f7b04702c3a64e65. Report an issue: GitHub.