{"record":{"id":"6101d1654ce04dd5","repo":"airbnb/epoxy","slug":"can-only-call-this-when-inside-the-buildmodels-method","errorCode":null,"errorMessage":"Can only call this when inside the `buildModels` method","messagePattern":"Can only call this when inside the `buildModels` method","errorType":"exception","errorClass":"IllegalEpoxyUsage","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyController.java","lineNumber":452,"sourceCode":"    interceptors.remove(interceptor);\n  }\n\n  /**\n   * Get the number of models added so far during the {@link #buildModels()} phase. It is only valid\n   * to call this from within that method.\n   * <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  /**","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyController.java#L434-L470","documentation":"assertIsBuildingModels() enforces that certain controller APIs (adding models, querying modelsBeingBuilt, counting built models, registering interceptor callbacks) are only used during the buildModels() pass. Using them outside throws IllegalEpoxyUsage because the building list is empty or otherwise invalid outside a build.","triggerScenarios":"Calling getFirstIndexOfModelInBuildingList, isModelAddedMultipleTimes, addAfterInterceptorCallback, getModelCountBuiltSoFar, or addInternal (e.g. model.addTo(controller)) outside of buildModels().","commonSituations":"Calling model.addTo(controller) from outside the controller's buildModels (e.g. in an adapter click handler or activity code); querying building-list state in a callback after the build finished.","solutions":["Move model.addTo(controller) calls into buildModels()","Wrap state queries so they only run during building (or guard with isBuildingModels())","Move addAfterInterceptorCallback registration to the start of a buildModels pass"],"exampleFix":"// before\nvoid onClick() {\n  new HeaderModel_().id(1).addTo(controller); // crash\n}\n// after\n@Override\nprotected void buildModels() {\n  new HeaderModel_().id(1).addTo(this);\n}","handlingStrategy":"type-guard","validationCode":"if (controller.isBuildingModels()) { model.addTo(controller); }","typeGuard":"boolean canAddModels(EpoxyController c) { return c.isBuildingModels(); }","tryCatchPattern":"try { model.addTo(controller); } catch (IllegalEpoxyUsage e) { /* move call into buildModels */ }","preventionTips":["Only use model.addTo(controller) inside buildModels","Centralize model construction in the controller's buildModels method"],"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"}