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

Typed3EpoxyController's `buildModels()` is final and forwards to the abstract `buildModels(data1, data2, data3)` only when `isBuildingModels()` is true, i.e. during a controller-initiated build pass. Direct calls throw `IllegalStateException` because models built outside the lifecycle won't be diffed/attached correctly.

Solutions

  1. Trigger building with `controller.setData(data1, data2, data3)`.
  2. In tests, submit data via `setData` or use Epoxy's testing helpers instead of calling the final `buildModels()`.
  3. Avoid calling `buildModels()` from constructors, other controllers, or background threads.
  4. Extract model construction into a pure helper function if you need the models independently of the controller lifecycle.

Example fix

// before
controller.buildModels(data1, data2, data3); // IllegalStateException

// after
controller.setData(data1, data2, data3);
Defensive patterns

Strategy: validation

Validate before calling

// Wrong: controller.buildModels(data1, data2, data3);
// Right:
controller.setData(data1, data2, data3);

Prevention

When it happens

Trigger: Calling `controller.buildModels()` or the three-arg override directly from app code or tests instead of going through `setData(data1, data2, data3)`.

Common situations: Tests invoking `buildModels()` to inspect generated models; eager model building in a constructor/init; calling `buildModels()` after mutating data as a shortcut for `setData`.

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


AI-assisted analysis of airbnb/epoxy@e45bd3a61f (2026-09-13). Data as JSON: /api/errors/d3694743692dd9ac. Report an issue: GitHub.

Appendix: source

Thrown at epoxy-adapter/src/main/java/com/airbnb/epoxy/Typed3EpoxyController.java:76

    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 "
              + "model refresh with new data.");
    }
    super.requestDelayedModelBuild(delayMs);
  }

  @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(data1, data2, data3);
  }

  protected abstract void buildModels(T data1, U data2, V data3);
}

View on GitHub (pinned to e45bd3a61f)