{"record":{"id":"40d403c66e9b9c19","repo":"airbnb/epoxy","slug":"cannot-call-this-from-inside-buildmodels","errorCode":null,"errorMessage":"Cannot call this from inside `buildModels`","messagePattern":"Cannot call this from inside `buildModels`","errorType":"exception","errorClass":"IllegalEpoxyUsage","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyController.java","lineNumber":458,"sourceCode":"   * <p>\n   * This is different from the number of models currently on the adapter, since models on the\n   * adapter are not updated until after models are finished being built. To access current adapter\n   * count call {@link #getAdapter()} and {@link EpoxyControllerAdapter#getItemCount()}\n   */\n  protected int getModelCountBuiltSoFar() {\n    assertIsBuildingModels();\n    return modelsBeingBuilt.size();\n  }\n\n  private void assertIsBuildingModels() {\n    if (!isBuildingModels()) {\n      throw new IllegalEpoxyUsage(\"Can only call this when inside the `buildModels` method\");\n    }\n  }\n\n  private void assertNotBuildingModels() {\n    if (isBuildingModels()) {\n      throw new IllegalEpoxyUsage(\"Cannot call this from inside `buildModels`\");\n    }\n  }\n\n  /**\n   * Add the model to this controller. Can only be called from inside {@link\n   * EpoxyController#buildModels()}.\n   */\n  public void add(@NonNull EpoxyModel<?> model) {\n    model.addTo(this);\n  }\n\n  /**\n   * Add the models to this controller. Can only be called from inside {@link\n   * EpoxyController#buildModels()}.\n   */\n  protected void add(@NonNull EpoxyModel<?>... modelsToAdd) {\n    modelsBeingBuilt.ensureCapacity(modelsBeingBuilt.size() + modelsToAdd.length);\n","sourceCodeStart":440,"sourceCodeEnd":476,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyController.java#L440-L476","documentation":"assertNotBuildingModels() enforces that controller APIs which mutate or query the already-built model list (setDebugLoggingEnabled, moveModel, notifyModelChanged) are not called during an in-progress buildModels() pass, since the built list is mid-flight. Violating this throws IllegalEpoxyUsage.","triggerScenarios":"Calling moveModel, notifyModelChanged, or setDebugLoggingEnabled from inside buildModels() (directly or from code invoked by buildModels).","commonSituations":"Reordering or refreshing models inside buildModels in response to data; calling notifyModelChanged from a model's click handler that executes during building; enabling debug logging in a build-time callback.","solutions":["Call moveModel/notifyModelChanged only after buildModels completes (post to main thread if needed)","Replace in-build moveModel with ordering the models correctly during building","Enable setDebugLoggingEnabled once at controller setup, not during builds"],"exampleFix":"// before\n@Override\nprotected void buildModels() {\n  moveModel(model, 0); // crash\n  ...\n}\n// after\nvoid onReady() {\n  moveModel(model, 0);\n}","handlingStrategy":"type-guard","validationCode":"if (!controller.isBuildingModels()) { controller.moveModel(model, toIndex); }","typeGuard":"boolean canMutateBuiltList(EpoxyController c) { return !c.isBuildingModels(); }","tryCatchPattern":"try { controller.notifyModelChanged(model); } catch (IllegalEpoxyUsage e) { /* post until after build */ }","preventionTips":["Issue moveModel/notifyModelChanged from post-build callbacks or main-thread posts","Configure debug logging at controller initialization time only"],"tags":["android","epoxy","illegal-usage","lifecycle"],"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"}