DrKLO/Telegram · error · IllegalArgumentException

Item at position {} requires {} spans but GridLayoutManager

Error message

Item at position {} requires {} spans but GridLayoutManager has only {} spans.

What it means

GridLayoutManager assigns each item a span size via SpanSizeLookup.getSpanSize (default 1). During fill, it walks positions until the row/column span budget (mSpanCount) is consumed. If a single item's spanSize exceeds the total mSpanCount, the item can never fit in any row, so the layout is mathematically impossible and GridLayoutManager throws rather than entering an infinite loop or mis-rendering. The classic case is a 'full-width' header item with spanSize = spanCount while the grid was configured with a smaller spanCount than the lookup expects.

Source

Thrown at TMessagesProj/src/main/java/androidx/recyclerview/widget/GridLayoutManager.java:541

        // where they may have a header row which should be laid out according to children.
        if (flexibleInOtherDir) {
            updateMeasurements(); //  reset measurements
        }
        final boolean layingOutInPrimaryDirection =
                layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
        int count = 0;
        int consumedSpanCount = 0;
        int remainingSpan = mSpanCount;
        if (!layingOutInPrimaryDirection) {
            int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition);
            int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition);
            remainingSpan = itemSpanIndex + itemSpanSize;
        }
        while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
            int pos = layoutState.mCurrentPosition;
            final int spanSize = getSpanSize(recycler, state, pos);
            if (spanSize > mSpanCount) {
                throw new IllegalArgumentException("Item at position " + pos + " requires "
                        + spanSize + " spans but GridLayoutManager has only " + mSpanCount
                        + " spans.");
            }
            remainingSpan -= spanSize;
            if (remainingSpan < 0) {
                break; // item did not fit into this row or column
            }
            View view = layoutState.next(recycler);
            if (view == null) {
                break;
            }
            consumedSpanCount += spanSize;
            mSet[count] = view;
            count++;
        }

        if (count == 0) {
            result.mFinished = true;

View on GitHub (pinned to 45ab8f4308)

Solutions

  1. Make SpanSizeLookup.getSpanSize return values bounded by the current GridLayoutManager.getSpanCount(), e.g. return getSpanCount() for full-width items.
  2. When changing spanCount at runtime, ensure the SpanSizeLookup logic scales with it.
  3. Use a header SpanSizeLookup that returns spanCount (the dynamic total), not a hardcoded number.

Example fix

// before
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override public int getSpanSize(int position) {
        return isHeader(position) ? 4 : 1; // throws if spanCount < 4
    }
});

// after
final GridLayoutManager glm = ...;
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override public int getSpanSize(int position) {
        return isHeader(position) ? glm.getSpanCount() : 1;
    }
});
Defensive patterns

Strategy: validation

Validate before calling

// Bound span size to the current span count
final GridLayoutManager glm = ...;
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override public int getSpanSize(int position) {
        if (isHeader(position)) return glm.getSpanCount(); // dynamic
        int s = itemSpanSize(position);
        return Math.min(Math.max(1, s), glm.getSpanCount());
    }
});

Prevention

When it happens

Trigger: A SpanSizeLookup that returns spanCount for headers but the GridLayoutManager was created with a smaller spanCount (e.g. 3 vs lookup assuming 4); dynamic span count change (rotation, tablet) without recomputing span sizes; a lookup returning a hardcoded number larger than the configured span count.

Common situations: Mixed-item feeds (headers + grid items) where the header span size was hardcoded; rotation changing spanCount from 3 to 2 but SpanSizeLookup still returns 3; tablet/master-detail where span count varies but span sizes do not.

Related errors


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