{"record":{"id":"b142f99b8ea90133","repo":"airbnb/epoxy","slug":"you-cannot-call-buildmodels-directly-call-setdata-instead-to-b142f9","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/TypedEpoxyController.java","lineNumber":73,"sourceCode":"  @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  @Nullable\n  public final T getCurrentData() {\n    return currentData;\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(currentData);\n  }\n\n  protected abstract void buildModels(T data);\n}\n","sourceCodeStart":55,"sourceCodeEnd":82,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/TypedEpoxyController.java#L55-L82","documentation":"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).","triggerScenarios":"Calling controller.buildModels() directly on a TypedEpoxyController outside an active build triggered by setData.","commonSituations":"Calling buildModels after setData from UI code expecting an immediate rebuild; migrating from SimpleEpoxyController where buildModels is the normal override.","solutions":["Call controller.setData(data) to trigger the rebuild","Remove any manual buildModels() invocations; keep only the abstract buildModels(T data) override","Use controller.requestModelBuild() alternatives only if you switch to a base EpoxyController"],"exampleFix":"// before\ncontroller.buildModels();\n// after\ncontroller.setData(myData);","handlingStrategy":"validation","validationCode":"if (controller is TypedEpoxyController<*>) controller.setData(data) else controller.buildModels()","typeGuard":"fun isTypedController(c: EpoxyController) = c is TypedEpoxyController<*>","tryCatchPattern":null,"preventionTips":["Treat buildModels as private in typed controllers","Only override the abstract buildModels(T) in subclasses","Add code-review checklist item for controller migration"],"tags":["android","epoxy","illegal-state","api-misuse"],"backgroundTag":"invalid-state-transition","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"}