{"record":{"id":"f17b9d286f367f5b","repo":"airbnb/epoxy","slug":"models-cannot-be-changed-once-they-are-added-to-the","errorCode":null,"errorMessage":"Models cannot be changed once they are added to the controller","messagePattern":"Models cannot be changed once they are added to the controller","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/ControllerModelList.java","lineNumber":15,"sourceCode":"package com.airbnb.epoxy;\n\n/**\n * This ArrayList subclass enforces that no changes are made to the list after {@link #freeze()} is\n * called. This prevents model interceptors from storing the list and trying to change it later. We\n * could copy the list before diffing, but that would waste memory to make the copy for every\n * buildModels cycle, plus the interceptors could still try to modify the list and be confused about\n * why it doesn't do anything.\n */\nclass ControllerModelList extends ModelList {\n\n  private static final ModelListObserver OBSERVER = new ModelListObserver() {\n    @Override\n    public void onItemRangeInserted(int positionStart, int itemCount) {\n      throw new IllegalStateException(\n          \"Models cannot be changed once they are added to the controller\");\n    }\n\n    @Override\n    public void onItemRangeRemoved(int positionStart, int itemCount) {\n      throw new IllegalStateException(\n          \"Models cannot be changed once they are added to the controller\");\n    }\n  };\n\n  ControllerModelList(int expectedModelCount) {\n    super(expectedModelCount);\n    pauseNotifications();\n  }\n\n  void freeze() {\n    setObserver(OBSERVER);\n    resumeNotifications();","sourceCodeStart":1,"sourceCodeEnd":33,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/ControllerModelList.java#L1-L33","documentation":"ControllerModelList is the list backing an EpoxyController after models have been built and handed off to the adapter; its observer forbids insertions because mutating built models invalidates the diffing/update pipeline. Any call that inserts into the list after buildModels output is finalized throws this IllegalStateException.","triggerScenarios":"Mutating the controller's model list after buildModels() completes — e.g. adding to the list in a click callback, holding a reference to the controller's model list and calling add/addAll later, or mutating models inside onException/interceptors after finalization.","commonSituations":"Retaining the model list from buildModels and appending items in an async callback; calling add() on a list captured from a controller instead of building new models in buildModels().","solutions":["Do all model additions inside buildModels() — call requestModelBuild() to rebuild instead of mutating the list.","Keep model data in separate fields; mutate the data, then trigger requestModelBuild().","Never hold references to the controller's internal model list outside buildModels()."],"exampleFix":"// before\nList<EpoxyModel<?>> models = new ControllerModelList(...); // captured\nbutton.setOnClickListener(v -> models.add(newModel())); // throws\n\n// after\nbutton.setOnClickListener(v -> {\n  items.add(newItem());\n  controller.requestModelBuild(); // rebuild models in buildModels()\n});","handlingStrategy":"type-guard","validationCode":"// never mutate ControllerModelList outside buildModels(); mutate your own data list instead","typeGuard":null,"tryCatchPattern":"try { models.add(m); } catch (IllegalStateException e) { requestModelBuild(); }","preventionTips":["Treat the controller model list as write-once inside buildModels().","Use requestModelBuild() for every post-build change.","Keep app data in separate collections.","Avoid storing references to controller.getModelList()/the built list."],"tags":["android","epoxy","controller","immutable-list","model-mutation"],"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"}