{"record":{"id":"96d84c50922a6616","repo":"airbnb/epoxy","slug":"you-must-enable-diffing-before-notifying-models-changed","errorCode":null,"errorMessage":"You must enable diffing before notifying models changed","messagePattern":"You must enable diffing before notifying models changed","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyAdapter.java","lineNumber":79,"sourceCode":"    return model.isShown() ? model : hiddenModel;\n  }\n\n  /**\n   * Intelligently notify item changes by comparing the current {@link #models} list against the\n   * previous so you don't have to micromanage notification calls yourself. This may be\n   * prohibitively slow for large model lists (in the hundreds), in which case consider doing\n   * notification calls yourself. If you use this, all your view models must implement {@link\n   * EpoxyModel#hashCode()} and {@link EpoxyModel#equals(Object)} to completely identify their\n   * state, so that changes to a model's content can be detected. Before using this you must enable\n   * it with {@link #enableDiffing()}, since keeping track of the model state adds extra computation\n   * time to all other data change notifications.\n   *\n   * @see #enableDiffing()\n   */\n\n  protected void notifyModelsChanged() {\n    if (diffHelper == null) {\n      throw new IllegalStateException(\"You must enable diffing before notifying models changed\");\n    }\n\n    diffHelper.notifyModelChanges();\n  }\n\n  /**\n   * Notify that the given model has had its data changed. It should only be called if the model\n   * retained the same position.\n   */\n  protected void notifyModelChanged(EpoxyModel<?> model) {\n    notifyModelChanged(model, null);\n  }\n\n  /**\n   * Notify that the given model has had its data changed. It should only be called if the model\n   * retained the same position.\n   */\n  protected void notifyModelChanged(EpoxyModel<?> model, @Nullable Object payload) {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyAdapter.java#L61-L97","documentation":"EpoxyAdapter throws this IllegalStateException when notifyModelsChanged() is called while diffing has never been enabled (diffHelper is null). Diffing is an opt-in feature; without it there is no DiffHelper to compute model changes, so notifying would be incorrect. The library forces you to call enableDiffing() first.","triggerScenarios":"Calling the protected notifyModelsChanged() (directly or via helpers like insertModelBefore/insertModelAfter/getAllModelsAfter paths that rely on notifications) on an EpoxyAdapter subclass that never called enableDiffing().","commonSituations":"Subclassing EpoxyAdapter manually (instead of using EpoxyController) and calling model-mutation helpers that expect diffing; copying adapter code that assumes diffing was enabled in a base class or setup method that was skipped.","solutions":["Call enableDiffing() in your adapter's constructor before any model notifications occur","Verify your adapter setup path actually runs enableDiffing() (check that it isn't gated behind a flag or skipped branch)","Prefer migrating to EpoxyController/EpoxyRecyclerView with controllers, which manage diffing automatically"],"exampleFix":"// before\nclass MyAdapter extends EpoxyAdapter {\n  MyAdapter() { /* no enableDiffing */ }\n  void update() { notifyModelsChanged(); }\n}\n// after\nclass MyAdapter extends EpoxyAdapter {\n  MyAdapter() { enableDiffing(); }\n  void update() { notifyModelsChanged(); }\n}","handlingStrategy":"validation","validationCode":"if (adapter.getModelPosition(model) >= 0 || adapter.getClass().isAnnotationPresent(DiffingEnabled.class)) { /* ensure enableDiffing() called in constructor */ }","typeGuard":null,"tryCatchPattern":"try { adapter.notifyModelsChanged(); } catch (IllegalStateException e) { adapter.enableDiffing(); adapter.notifyModelsChanged(); }","preventionTips":["Always call enableDiffing() as the first statement in your EpoxyAdapter subclass constructor","Prefer EpoxyController-based APIs over manual adapter mutation"],"tags":["android","epoxy","diffing","illegal-state"],"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"}