airbnb/epoxy · error · IllegalEpoxyUsage
Cannot call `requestDelayedModelBuild` from inside…
Error message
Cannot call `requestDelayedModelBuild` from inside `buildModels`
What it means
requestDelayedModelBuild(delayMs) schedules a debounced buildModels() pass; invoking it while buildModels() is already running would queue a build inside the build being executed, so an IllegalEpoxyUsage is thrown. Like requestModelBuild, it must be called outside model building.
Solutions
- Call requestDelayedModelBuild() only outside buildModels (e.g. after data updates)
- Replace in-build scheduling with addModelBuildListener + post-build request
- If using moveModel, ensure it is invoked outside the buildModels pass
Example fix
// before
@Override
protected void buildModels() {
requestDelayedModelBuild(100); // crash
...
}
// after
void onDataChanged() {
requestDelayedModelBuild(100);
}
@Override
protected void buildModels() { ... } Defensive patterns
Strategy: try-catch
Validate before calling
if (!controller.isBuildingModels()) { controller.requestDelayedModelBuild(delayMs); } Try / catch
try { controller.requestDelayedModelBuild(100); } catch (IllegalEpoxyUsage e) { /* defer until after current build */ } Prevention
- Schedule delayed builds from data-update paths, never inside buildModels
- Call moveModel outside of the building pass
When it happens
Trigger: Calling requestDelayedModelBuild() (directly, or via moveModel) from within buildModels().
Common situations: Scheduling a delayed refresh from inside buildModels; calling controller.moveModel(...) during building instead of outside it; chaining a delayed build from logic invoked by buildModels.
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 `requestModelBuild` from inside `buildModels`
- Can only call this when inside the `buildModels` method
- Cannot call this from inside `buildModels`
- You cannot hide a model in an EpoxyController. Use `addIf`…
- Notifications already paused
AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13).
Data as JSON: /api/errors/1180f664e23f6e6f.
Report an issue: GitHub.
Appendix: source
Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyController.java:225
* <p>
* Using this to delay a model update may be helpful in cases where user input is causing many
* rapid changes in the models, such as typing. In that case, the view is already updated on
* screen and constantly rebuilding models is potentially slow and unnecessary. The downside to
* delaying the model build too long is that models will not be in sync with the data or view, and
* scrolling the view offscreen and back onscreen will cause the model to bind old data.
* <p>
* If a previous request is still pending it will be removed in favor of this new delay
* <p>
* Any call to {@link #requestModelBuild()} will override a delayed request.
* <p>
* In most cases you should use {@link #requestModelBuild()} instead of this.
*
* @param delayMs The time in milliseconds to delay the model build by. Should be greater than or
* equal to 0. A value of 0 is equivalent to calling {@link #requestModelBuild()}
*/
public synchronized void requestDelayedModelBuild(int delayMs) {
if (isBuildingModels()) {
throw new IllegalEpoxyUsage(
"Cannot call `requestDelayedModelBuild` from inside `buildModels`");
}
if (requestedModelBuildType == RequestedModelBuildType.DELAYED) {
cancelPendingModelBuild();
} else if (requestedModelBuildType == RequestedModelBuildType.NEXT_FRAME) {
return;
}
requestedModelBuildType =
delayMs == 0 ? RequestedModelBuildType.NEXT_FRAME : RequestedModelBuildType.DELAYED;
modelBuildHandler.postDelayed(buildModelsRunnable, delayMs);
}
/**
* Cancels a pending call to {@link #buildModels()} if one has been queued by {@link
* #requestModelBuild()}.View on GitHub (pinned to e45bd3a61f)