{"record":{"id":"d3694743692dd9ac","repo":"airbnb/epoxy","slug":"you-cannot-call-buildmodels-directly-call-setdata-instead-to-d36947","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/Typed3EpoxyController.java","lineNumber":76,"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, data3);\n  }\n\n  protected abstract void buildModels(T data1, U data2, V data3);\n}\n","sourceCodeStart":58,"sourceCodeEnd":85,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/Typed3EpoxyController.java#L58-L85","documentation":"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.","triggerScenarios":"Calling `controller.buildModels()` or the three-arg override directly from app code or tests instead of going through `setData(data1, data2, data3)`.","commonSituations":"Tests invoking `buildModels()` to inspect generated models; eager model building in a constructor/init; calling `buildModels()` after mutating data as a shortcut for `setData`.","solutions":["Trigger building with `controller.setData(data1, data2, data3)`.","In tests, submit data via `setData` or use Epoxy's testing helpers instead of calling the final `buildModels()`.","Avoid calling `buildModels()` from constructors, other controllers, or background threads.","Extract model construction into a pure helper function if you need the models independently of the controller lifecycle."],"exampleFix":"// before\ncontroller.buildModels(data1, data2, data3); // IllegalStateException\n\n// after\ncontroller.setData(data1, data2, data3);","handlingStrategy":"validation","validationCode":"// Wrong: controller.buildModels(data1, data2, data3);\n// Right:\ncontroller.setData(data1, data2, data3);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat buildModels overrides as framework-invoked only.","Use setData (or Epoxy test helpers) in tests to produce models.","Extract model construction into pure helpers if you need models outside the build pass."],"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"}