airbnb/epoxy · error · UnsupportedOperationException
UnboundedViewPool does not support setting a maximum number…
Error message
UnboundedViewPool does not support setting a maximum number of recycled views
What it means
UnboundedViewPool is a RecyclerView.ViewHolder pool with no per-viewType size cap, implemented by overriding Epoxy's ViewPool. setMaxRecycledViews is intentionally unsupported because a cap contradicts the pool's unbounded design, so calling it always throws UnsupportedOperationException.
Solutions
- Remove the setMaxRecycledViews call when using UnboundedViewPool — it is unbounded by design.
- Use a bounded pool (e.g. standard RecyclerView.RecycledViewPool or Epoxy's default ViewPool) if you need per-type caps.
- Branch on pool type before configuring: only call setMaxRecycledViews when the pool supports it.
- Control memory instead by limiting how many holders RecyclerView can create (layout manager hints) or using a different pool.
Example fix
// before
val pool = UnboundedViewPool()
pool.setMaxRecycledViews(0, 5) // throws
// after
val pool: RecyclerView.RecycledViewPool =
if (needCaps) RecyclerView.RecycledViewPool().apply { setMaxRecycledViews(0, 5) }
else UnboundedViewPool() Defensive patterns
Strategy: type-guard
Type guard
fun RecyclerView.RecycledViewPool.setMaxIfSupported(viewType: Int, max: Int) {
if (this is UnboundedViewPool) return
setMaxRecycledViews(viewType, max)
} Prevention
- Treat UnboundedViewPool as configure-free: never call sizing APIs on it.
- Centralize pool creation so bounded/unbounded choice and configuration live in one place.
- If you need caps, use the standard pool — don't fight the unbounded design.
When it happens
Trigger: Calling setMaxRecycledViews(viewType, max) on an UnboundedViewPool instance, directly or via code that configures a generic ViewPool/RecycledViewPool without knowing the concrete type.
Common situations: Shared pool-configuration utility code that sets max sizes for every pool including unbounded ones; swapping a bounded RecycledViewPool for UnboundedViewPool to fix memory churn while keeping the old sizing calls; copy-pasted RecyclerView setup.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Diffing is enabled. You should use notifyModelsChanged…
- Moving more than 1 item at a time is not supported. Number…
- You should set a layout with layout(...) instead of using…
- Layout resources are unsupported. Views must be created…
- Could not find model for view type:
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/3ebdb6119a1404dd.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/UnboundedViewPool.kt:24
import java.util.LinkedList
import java.util.Queue
/**
* Like its parent, UnboundedViewPool lets you share Views between multiple RecyclerViews. However
* there is no maximum number of recycled views that it will store. This usually ends up being
* optimal, barring any hard memory constraints, as RecyclerViews do not recycle more Views than
* they need.
*/
internal class UnboundedViewPool : RecycledViewPool() {
private val scrapHeaps = SparseArray<Queue<ViewHolder>>()
override fun clear() {
scrapHeaps.clear()
}
override fun setMaxRecycledViews(viewType: Int, max: Int) {
throw UnsupportedOperationException(
"UnboundedViewPool does not support setting a maximum number of recycled views"
)
}
override fun getRecycledView(viewType: Int): ViewHolder? {
val scrapHeap = scrapHeaps.get(viewType)
return scrapHeap?.poll()
}
override fun putRecycledView(viewHolder: ViewHolder) {
getScrapHeapForType(viewHolder.itemViewType).add(viewHolder)
}
override fun getRecycledViewCount(viewType: Int): Int {
return scrapHeaps.get(viewType)?.size ?: 0
}
private fun getScrapHeapForType(viewType: Int): Queue<ViewHolder> {View on GitHub (pinned to e45bd3a61f)