{"record":{"id":"666b4776604a13da","repo":"airbnb/epoxy","slug":"cannot-change-a-model-s-id-after-it-has-been-added-to-the","errorCode":null,"errorMessage":"Cannot change a model's id after it has been added to the adapter.","messagePattern":"Cannot change a model's id after it has been added to the adapter\\.","errorType":"exception","errorClass":"IllegalEpoxyUsage","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java","lineNumber":238,"sourceCode":"      @FloatRange(from = 0.0f, to = 100.0f) float percentVisibleWidth,\n      @Px int visibleHeight,\n      @Px int visibleWidth,\n      @NonNull T view\n  ) {\n  }\n\n  public long id() {\n    return id;\n  }\n\n  /**\n   * Override the default id in cases where the data subject naturally has an id, like an object\n   * from a database. This id can only be set before the model is added to the adapter, it is an\n   * error to change the id after that.\n   */\n  public EpoxyModel<T> id(long id) {\n    if ((addedToAdapter || firstControllerAddedTo != null) && id != this.id) {\n      throw new IllegalEpoxyUsage(\n          \"Cannot change a model's id after it has been added to the adapter.\");\n    }\n\n    hasDefaultId = false;\n    this.id = id;\n    return this;\n  }\n\n  /**\n   * Use multiple numbers as the id for this model. Useful when you don't have a single long that\n   * represents a unique id.\n   * <p>\n   * This hashes the numbers, so there is a tiny risk of collision with other ids.\n   */\n  public EpoxyModel<T> id(@Nullable Number... ids) {\n    long result = 0;\n    if (ids != null) {\n      for (@Nullable Number id : ids) {","sourceCodeStart":220,"sourceCodeEnd":256,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/EpoxyModel.java#L220-L256","documentation":"Epoxy models get an id used by the adapter for diffing and stable item identity. Once the model has been added to an adapter (or its controller has been built), changing the id would silently break diffing, so Epoxy throws IllegalEpoxyUsage.","triggerScenarios":"Calling model.id(long) (or id(long,...)) after the model was already added to an adapter or built by a controller, i.e. when addedToAdapter is true or firstControllerAddedTo is non-null and the new id differs from the current one.","commonSituations":"Reusing a model instance across buildModels passes and re-setting its id; mutating a retained model's id inside an interceptor after models were set on the adapter; caching a model in an Activity field and updating its id on new data.","solutions":["Create a new model instance instead of mutating the id of an already-added one","Set the id once at model creation time, before the model reaches the controller/adapter","Call controller.requestModelBuild() so buildModels recreates the model with the new id","Only mutate models inside buildModels (or an interceptor before the adapter is set)"],"exampleFix":"// before\nprivate final HeaderModel_ header = new HeaderModel_();\nvoid update(long id) { header.id(id); } // throws after adapter set\n// after\nvoid update(long id) { requestModelBuild(); } // and in buildModels: new HeaderModel_().id(id)","handlingStrategy":"validation","validationCode":"if (model.addedToAdapter() /* or track via controller */) throw new IllegalStateException(\"Set id before adding to adapter\"); model.id(newId);","typeGuard":null,"tryCatchPattern":"try { model.id(newId); } catch (IllegalEpoxyUsage e) { requestModelBuild(); }","preventionTips":["Set ids at model construction, never on cached instances","Treat models as immutable once handed to buildModels","Use requestModelBuild() for id changes"],"tags":["android","epoxy","immutable-state","model-id"],"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"}