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

  1. Call controller.setData(data) to trigger the rebuild
  2. Remove any manual buildModels() invocations; keep only the abstract buildModels(T data) override
  3. 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

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


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)