airbnb/epoxy · error · IllegalStateException
You cannot call `requestModelBuild` directly. Call…
Error message
You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a model refresh with new data.
What it means
Typed2EpoxyController is a data-driven controller: you feed it data via `setData(data1, data2)`, and it manages when model builds run. `requestModelBuild()` is final and throws `IllegalStateException` unless `allowModelBuildRequests` is true (which is only true briefly inside `setData`). This prevents callers from triggering a rebuild with stale internal data instead of providing fresh data.
Solutions
- Call `controller.setData(newData1, newData2)` with the updated data instead of `requestModelBuild()`.
- Keep a copy of the current data in your screen/fragment, mutate it, and pass the full new pair back through `setData`.
- If you need a delayed rebuild, pass new data via `setData`; the controller schedules the build itself.
- Refactor the subclass's `buildModels(data1, data2)` to derive everything from the data parameters rather than mutable external state.
Example fix
// before items.add(newItem); controller.requestModelBuild(); // IllegalStateException // after items.add(newItem); controller.setData(items, otherData);
Defensive patterns
Strategy: validation
Validate before calling
// Typed2EpoxyController: refresh only through setData // if (controller is Typed2EpoxyController<*, *>) controller.setData(d1, d2) else controller.requestModelBuild()
Prevention
- Remember Typed* controllers are data-driven: any change goes through setData.
- Grep your codebase for `requestModelBuild()` and remove calls on Typed controllers.
- Avoid mutating objects already passed to setData; create and pass new data.
- When migrating controller types, update all refresh call sites.
When it happens
Trigger: Calling `controller.requestModelBuild()` from application code at any point other than indirectly through `setData`. For example, calling it after mutating an object that was previously passed to `setData`, hoping the controller will pick up the change.
Common situations: Porting code from base `EpoxyController` (where `requestModelBuild()` is the normal API) to a Typed controller without switching to `setData`; calling `requestModelBuild()` in a callback/listener after data changed outside the controller; copy-pasting refresh logic between typed and untyped controllers.
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`…
- You cannot call `requestModelBuild` directly. Call…
- Can only call this when inside the `buildModels` method
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/f3f25517827d3214.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/Typed2EpoxyController.java:47
super(modelBuildingHandler, diffingHandler);
}
/**
* Call this with the latest data when you want models to be rebuilt. The data will be passed on
* to {@link #buildModels(Object, Object)}
*/
public void setData(T data1, U data2) {
this.data1 = data1;
this.data2 = data2;
allowModelBuildRequests = true;
requestModelBuild();
allowModelBuildRequests = false;
}
@Override
public final void requestModelBuild() {
if (!allowModelBuildRequests) {
throw new IllegalStateException(
"You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a "
+ "model refresh with new data.");
}
super.requestModelBuild();
}
@Override
public void moveModel(int fromPosition, int toPosition) {
allowModelBuildRequests = true;
super.moveModel(fromPosition, toPosition);
allowModelBuildRequests = false;
}
@Override
public void requestDelayedModelBuild(int delayMs) {
if (!allowModelBuildRequests) {
throw new IllegalStateException(
"You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a "View on GitHub (pinned to e45bd3a61f)