airbnb/epoxy · error · IllegalEpoxyUsage
Can only call this when inside the `buildModels` method
Error message
Can only call this when inside the `buildModels` method
What it means
assertIsBuildingModels() enforces that certain controller APIs (adding models, querying modelsBeingBuilt, counting built models, registering interceptor callbacks) are only used during the buildModels() pass. Using them outside throws IllegalEpoxyUsage because the building list is empty or otherwise invalid outside a build.
Solutions
- Move model.addTo(controller) calls into buildModels()
- Wrap state queries so they only run during building (or guard with isBuildingModels())
- Move addAfterInterceptorCallback registration to the start of a buildModels pass
Example fix
// before
void onClick() {
new HeaderModel_().id(1).addTo(controller); // crash
}
// after
@Override
protected void buildModels() {
new HeaderModel_().id(1).addTo(this);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (controller.isBuildingModels()) { model.addTo(controller); } Type guard
boolean canAddModels(EpoxyController c) { return c.isBuildingModels(); } Try / catch
try { model.addTo(controller); } catch (IllegalEpoxyUsage e) { /* move call into buildModels */ } Prevention
- Only use model.addTo(controller) inside buildModels
- Centralize model construction in the controller's buildModels method
When it happens
Trigger: Calling getFirstIndexOfModelInBuildingList, isModelAddedMultipleTimes, addAfterInterceptorCallback, getModelCountBuiltSoFar, or addInternal (e.g. model.addTo(controller)) outside of buildModels().
Common situations: Calling model.addTo(controller) from outside the controller's buildModels (e.g. in an adapter click handler or activity code); querying building-list state in a callback after the build finished.
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
- Cannot call this from inside `buildModels`
- You cannot call `buildModels` directly. Call `setModels`…
- Cannot call `requestModelBuild` from inside `buildModels`
- Cannot call `requestDelayedModelBuild` from inside…
- You cannot hide a model in an EpoxyController. Use `addIf`…
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/6101d1654ce04dd5.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyController.java:452
interceptors.remove(interceptor);
}
/**
* Get the number of models added so far during the {@link #buildModels()} phase. It is only valid
* to call this from within that method.
* <p>
* This is different from the number of models currently on the adapter, since models on the
* adapter are not updated until after models are finished being built. To access current adapter
* count call {@link #getAdapter()} and {@link EpoxyControllerAdapter#getItemCount()}
*/
protected int getModelCountBuiltSoFar() {
assertIsBuildingModels();
return modelsBeingBuilt.size();
}
private void assertIsBuildingModels() {
if (!isBuildingModels()) {
throw new IllegalEpoxyUsage("Can only call this when inside the `buildModels` method");
}
}
private void assertNotBuildingModels() {
if (isBuildingModels()) {
throw new IllegalEpoxyUsage("Cannot call this from inside `buildModels`");
}
}
/**
* Add the model to this controller. Can only be called from inside {@link
* EpoxyController#buildModels()}.
*/
public void add(@NonNull EpoxyModel<?> model) {
model.addTo(this);
}
/**View on GitHub (pinned to e45bd3a61f)