airbnb/epoxy · error · IllegalStateException
Models were set with #setModels, they can not be rebuilt.
Error message
Models were set with #setModels, they can not be rebuilt.
What it means
EpoxyRecyclerView.requestModelBuild() cannot rebuild models when the controller is a SimpleEpoxyController created internally by setModels — its models were supplied explicitly and there is no build callback to re-run. Attempting this throws this IllegalStateException.
Solutions
- Re-call recyclerView.setModels(newModels) with a freshly built list
- Switch to recyclerView.withModels { ... } so requestModelBuild has a rebuild callback
- If using a TypedEpoxyController, call setData on the controller instead
Example fix
// before recyclerView.setModels(models); recyclerView.requestModelBuild(); // throws // after recyclerView.setModels(buildModelList()); // rebuild by re-setting models
Defensive patterns
Strategy: validation
Validate before calling
val controller = recyclerView.epoxyController if (controller != null && controller !is SimpleEpoxyController) recyclerView.requestModelBuild() else recyclerView.setModels(buildModelList())
Type guard
fun canRebuild(rv: EpoxyRecyclerView) = rv.epoxyController != null && rv.epoxyController !is SimpleEpoxyController
Try / catch
try { recyclerView.requestModelBuild() } catch (e: IllegalStateException) { recyclerView.setModels(buildModelList()) } Prevention
- Pick one model-supply style (withModels vs setModels) per RecyclerView
- Track whether setModels was used; refresh via setModels only
- Prefer controller-based setup for refreshable lists
When it happens
Trigger: Calling recyclerView.requestModelBuild() after models were set via recyclerView.setModels(...) (or modelsWithConstructors), which internally uses a SimpleEpoxyController.
Common situations: Mixing setModels usage with requestModelBuild for refresh; assuming requestModelBuild works universally like with controller-based setups.
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
- You cannot call `buildModels` directly. Call `setData`…
- You cannot call `requestModelBuild` directly. Call…
- You cannot call `buildModels` directly. Call `setData`…
- A controller must be set before requesting a model build.
- You must enable diffing before notifying models changed
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/7ab8ec559d8ed2f9.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyRecyclerView.kt:539
*/
fun buildModels(controller: EpoxyController)
}
/**
* Request that the currently set EpoxyController has its models rebuilt. You can use this to
* avoid saving your controller as a field.
*
* You cannot use this if your controller is a [TypedEpoxyController] or if you set
* models via [setModels]. In that case you must set data directly on the
* controller or set models again.
*/
fun requestModelBuild() {
if (epoxyController == null) {
throw IllegalStateException("A controller must be set before requesting a model build.")
}
if (epoxyController is SimpleEpoxyController) {
throw IllegalStateException("Models were set with #setModels, they can not be rebuilt.")
}
epoxyController!!.requestModelBuild()
}
/**
* Clear the currently set EpoxyController or Adapter as well as any models that are displayed.
*
* Any pending requests to the EpoxyController to build models are canceled.
*
* Any existing child views are recycled to the view pool.
*/
open fun clear() {
// The controller is cleared so the next time models are set we can create a fresh one.
epoxyController?.cancelPendingModelBuild()
epoxyController = null
// We use swapAdapter instead of setAdapter so that the view pool is not cleared.View on GitHub (pinned to e45bd3a61f)