DrKLO/Telegram · error · IllegalArgumentException

The view does not have a ViewHolder. You cannot pass arbitra

Error message

The view does not have a ViewHolder. You cannot pass arbitrary views to this method, they should be created by the Adapter

What it means

Recycler.bindViewToPosition throws IllegalArgumentException when the supplied view has no associated ViewHolder. bindViewToPosition is a LayoutManager-facing API that re-binds an existing adapter-created view to a new position; it looks up the view's ViewHolder via getChildViewHolderInt, and a null result means the view was never created by the Adapter (it is an arbitrary, non-RecyclerView view). Only views that passed through the adapter's onCreateViewHolder/onBindViewHolder lifecycle carry a ViewHolder (stored in LayoutParams.mViewHolder).

Source

Thrown at TMessagesProj/src/main/java/androidx/recyclerview/widget/RecyclerView.java:6058

        /**
         * Binds the given View to the position. The View can be a View previously retrieved via
         * {@link #getViewForPosition(int)} or created by
         * {@link Adapter#onCreateViewHolder(ViewGroup, int)}.
         * <p>
         * Generally, a LayoutManager should acquire its views via {@link #getViewForPosition(int)}
         * and let the RecyclerView handle caching. This is a helper method for LayoutManager who
         * wants to handle its own recycling logic.
         * <p>
         * Note that, {@link #getViewForPosition(int)} already binds the View to the position so
         * you don't need to call this method unless you want to bind this View to another position.
         *
         * @param view The view to update.
         * @param position The position of the item to bind to this View.
         */
        public void bindViewToPosition(@NonNull View view, int position) {
            ViewHolder holder = getChildViewHolderInt(view);
            if (holder == null) {
                throw new IllegalArgumentException("The view does not have a ViewHolder. You cannot"
                        + " pass arbitrary views to this method, they should be created by the "
                        + "Adapter" + exceptionLabel());
            }
            final int offsetPosition = mAdapterHelper.findPositionOffset(position);
            if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) {
                throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "
                        + "position " + position + "(offset:" + offsetPosition + ")."
                        + "state:" + mState.getItemCount() + exceptionLabel());
            }
            tryBindViewHolderByDeadline(holder, offsetPosition, position, FOREVER_NS);

            final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams();
            final LayoutParams rvLayoutParams;
            if (lp == null) {
                rvLayoutParams = (LayoutParams) generateDefaultLayoutParams();
                holder.itemView.setLayoutParams(rvLayoutParams);
            } else if (!checkLayoutParams(lp)) {
                rvLayoutParams = (LayoutParams) generateLayoutParams(lp);

View on GitHub (pinned to 45ab8f4308)

Solutions

  1. Only pass views obtained from Recycler.getViewForPosition(int) (or getRecycledViewPool) into bindViewToPosition — these carry a ViewHolder.
  2. In a custom LayoutManager, always obtain child views via the provided Recycler parameter, never via LayoutInflater or new View.
  3. If you must introduce a non-adapter view, do so outside the Recycler pipeline (e.g. as a separate decorated header managed by the LayoutManager, not bound through the adapter).

Example fix

// before (custom LayoutManager)
View v = LayoutInflater.from(ctx).inflate(R.layout.item, rv, false);
recycler.bindViewToPosition(v, pos); // v has no ViewHolder -> throw
// after
View v = recycler.getViewForPosition(pos); // created/bound by adapter, has VH
addView(v);
measureChildWithMargins(v, 0, 0);
Defensive patterns

Strategy: type-guard

Validate before calling

// Only bind views obtained from the Recycler
View v = recycler.getViewForPosition(pos); // guaranteed to have a ViewHolder
recycler.bindViewToPosition(v, pos);

Type guard

static boolean isAdapterCreatedView(View v, RecyclerView rv) {
  ViewGroup.LayoutParams lp = v.getLayoutParams();
  return lp instanceof RecyclerView.LayoutParams
      && ((RecyclerView.LayoutParams) lp).getViewHolder() != null;
}

Prevention

When it happens

Trigger: A custom LayoutManager passing an externally created View (not from Recycler.getViewForPosition) into bindViewToPosition; manually inflating a view and handing it to the Recycler; a view whose ViewHolder was stripped during recycling due to a bug.

Common situations: Custom LayoutManager misuse; experimenting with Recycler internals; a third-party library that injects views into the Recycler bypassing the adapter; recycled view whose LayoutParams were replaced.

Related errors


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