{"record":{"id":"a8700ea74e75d31f","repo":"airbnb/epoxy","slug":"you-cannot-call-buildmodels-directly-call-setdata-instead-to","errorCode":null,"errorMessage":"You cannot call `buildModels` directly. Call `setData` instead to trigger a model refresh with new data.","messagePattern":"You cannot call `buildModels` directly\\. Call `setData` instead to trigger a model refresh with new data\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/Typed2EpoxyController.java","lineNumber":74,"sourceCode":"    allowModelBuildRequests = true;\n    super.moveModel(fromPosition, toPosition);\n    allowModelBuildRequests = false;\n  }\n\n  @Override\n  public void requestDelayedModelBuild(int delayMs) {\n    if (!allowModelBuildRequests) {\n      throw new IllegalStateException(\n          \"You cannot call `requestModelBuild` directly. Call `setData` instead to trigger a \"\n              + \"model refresh with new data.\");\n    }\n    super.requestDelayedModelBuild(delayMs);\n  }\n\n  @Override\n  protected final void buildModels() {\n    if (!isBuildingModels()) {\n      throw new IllegalStateException(\n          \"You cannot call `buildModels` directly. Call `setData` instead to trigger a model \"\n              + \"refresh with new data.\");\n    }\n    buildModels(data1, data2);\n  }\n\n  protected abstract void buildModels(T data1, U data2);\n}\n","sourceCodeStart":56,"sourceCodeEnd":83,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/Typed2EpoxyController.java#L56-L83","documentation":"Typed2EpoxyController's `buildModels()` is final and only delegates to the abstract `buildModels(data1, data2)` when `isBuildingModels()` is true — i.e. during a build pass the controller itself started. Otherwise it throws `IllegalStateException`, catching any attempt to invoke the model-building phase directly. Model building must be initiated by `setData`, not by callers.","triggerScenarios":"Calling `controller.buildModels()` (or the two-arg `buildModels(data1, data2)` override from outside) in app code, tests, or another controller to force models to be generated outside a legitimate build pass.","commonSituations":"Unit tests invoking `buildModels()` directly to assert on produced models; a developer calling `buildModels()` after updating data instead of calling `setData`; a subclass constructor or init block calling `buildModels()` eagerly to prime the adapter.","solutions":["Call `controller.setData(data1, data2)` to trigger `buildModels(data1, data2)` through the proper lifecycle.","In tests, use `setData` (or Epoxy's test helpers / `EpoxyController#buildModels` test utilities) rather than calling the final `buildModels()`.","Never call `buildModels()` from constructors, init blocks, or other controllers.","If you need models without an adapter, extract your model-building logic into a plain function returning a list and have `buildModels` add from it."],"exampleFix":"// before\nList<EpoxyModel<?>> models = new ArrayList<>();\ncontroller.buildModels(); // IllegalStateException\n\n// after\ncontroller.setData(data1, data2); // runs buildModels(data1, data2) safely","handlingStrategy":"validation","validationCode":"// Wrong: controller.buildModels();\n// Right:\ncontroller.setData(data1, data2); // safely runs buildModels(data1, data2)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never invoke `buildModels` — it's a protected lifecycle override, not a public refresh API.","In unit tests, assert on models by driving `setData` or extracting pure builder functions.","Don't call buildModels from constructors or other controllers."],"tags":["android","epoxy","lifecycle","api-misuse"],"backgroundTag":"unsupported-operation","analyzedSha":"e45bd3a61fe3a1f130e184f5b8dcf172ab99025a","analyzedAt":"2026-09-13T03:24:16.052Z","contentChangedAt":"2026-09-13T03:24:16.052Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}