airbnb/epoxy · error · IllegalStateException

No view exists at position $position

Error message

No view exists at position $position

What it means

When resetting a ViewStub, removeCurrentView first removes whatever inflated view currently occupies the stub's position in the parent ViewGroup. If getChildAt(position) returns null, the position holds no view, so resetStub cannot proceed and throws this IllegalStateException rather than silently corrupting view ordering.

Solutions

  1. Only call resetStub for stub positions that currently hold an inflated view; check viewGroup.childCount first.
  2. Ensure the ViewGroup contents are managed exclusively by ModelGroupHolder — no external add/removeView calls.
  3. Rebind the group instead of resetting an already-cleared stub slot.
  4. If the view was removed elsewhere, re-add it via the normal bind path rather than resetStub.

Example fix

// before
holder.resetStub(position) // throws if no view at position

// after
if (position < viewGroup.childCount && viewGroup.getChildAt(position) != null) {
  holder.resetStub(position)
}
Defensive patterns

Strategy: validation

Validate before calling

fun canResetStub(parent: ViewGroup, position: Int) =
  position in 0 until parent.childCount && parent.getChildAt(position) != null

Prevention

When it happens

Trigger: Calling resetStub on a stub slot after the inflated view was already removed or never inflated; position drift caused by views being added/removed from the parent ViewGroup out of band.

Common situations: Rebinding a model group where a stub was reset earlier and its view recycled away; external code removing child views from the group's container; calling resetStub before the stub was inflated.

Related errors


AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13). Data as JSON: /api/errors/07fb0262af24a3f5. Report an issue: GitHub.

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/ModelGroupHolder.kt:238

        if (inflatedId != View.NO_ID) {
            view.id = inflatedId
        }

        if (useStubLayoutParams) {
            viewGroup.addView(view, position, viewStub.layoutParams)
        } else {
            viewGroup.addView(view, position)
        }
    }

    fun resetStub() {
        removeCurrentView()
        viewGroup.addView(viewStub, position)
    }

    private fun removeCurrentView() {
        val view = viewGroup.getChildAt(position)
            ?: throw IllegalStateException("No view exists at position $position")
        viewGroup.removeView(view)
    }
}

/**
 * Local pool to the [ModelGroupHolder]
 */
private class LocalGroupRecycledViewPool : RecyclerView.RecycledViewPool()

/**
 * A viewholder's viewtype can only be set internally in an adapter when the viewholder
 * is created. To work around that we do the creation in an adapter.
 */
private class HelperAdapter : RecyclerView.Adapter<EpoxyViewHolder>() {

    private var model: EpoxyModel<*>? = null
    private var modelGroupParent: ViewParent? = null

View on GitHub (pinned to e45bd3a61f)