airbnb/epoxy · error · IllegalStateException
You cannot call `buildModels` directly. Call `setData`…
Error message
You cannot call `buildModels` directly. Call `setData` instead to trigger a model refresh with new data.
What it means
TypedEpoxyController makes buildModels() final and throws this IllegalStateException if it is invoked when no model build is in progress (isBuildingModels() is false). The public entry point for rebuilding models is setData, which calls the abstract buildModels(currentData).
Solutions
- Call controller.setData(data) to trigger the rebuild
- Remove any manual buildModels() invocations; keep only the abstract buildModels(T data) override
- Use controller.requestModelBuild() alternatives only if you switch to a base EpoxyController
Example fix
// before controller.buildModels(); // after controller.setData(myData);
Defensive patterns
Strategy: validation
Validate before calling
if (controller is TypedEpoxyController<*>) controller.setData(data) else controller.buildModels()
Type guard
fun isTypedController(c: EpoxyController) = c is TypedEpoxyController<*>
Prevention
- Treat buildModels as private in typed controllers
- Only override the abstract buildModels(T) in subclasses
- Add code-review checklist item for controller migration
When it happens
Trigger: Calling controller.buildModels() directly on a TypedEpoxyController outside an active build triggered by setData.
Common situations: Calling buildModels after setData from UI code expecting an immediate rebuild; migrating from SimpleEpoxyController where buildModels is the normal override.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- You cannot call `buildModels` directly. Call `setData`…
- You cannot call `requestModelBuild` directly. Call…
- A controller must be set before requesting a model build.
- Models were set with #setModels, they can not be rebuilt.
- You must enable diffing before notifying models changed
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/b142f99b8ea90133.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/TypedEpoxyController.java:73
@Override
public void requestDelayedModelBuild(int delayMs) {
if (!allowModelBuildRequests) {
throw new IllegalStateException(
"You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a "
+ "model refresh with new data.");
}
super.requestDelayedModelBuild(delayMs);
}
@Nullable
public final T getCurrentData() {
return currentData;
}
@Override
protected final void buildModels() {
if (!isBuildingModels()) {
throw new IllegalStateException(
"You cannot call `buildModels` directly. Call `setData` instead to trigger a model "
+ "refresh with new data.");
}
buildModels(currentData);
}
protected abstract void buildModels(T data);
}
View on GitHub (pinned to e45bd3a61f)