{"record":{"id":"241eee67f20e4bcc","repo":"airbnb/epoxy","slug":"two-models-have-the-same-id-id-s-must-be-unique-model-at","errorCode":null,"errorMessage":"Two models have the same ID. ID's must be unique! Model at position {position}: {model} Model at position {previousPosition}: {previousModel}","messagePattern":"Two models have the same ID\\. ID's must be unique! Model at position (.+?): (.+?) Model at position (.+?): (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/DiffHelper.java","lineNumber":241,"sourceCode":"\n    int modelCount = adapter.getCurrentModels().size();\n    currentStateList.ensureCapacity(modelCount);\n\n    for (int i = 0; i < modelCount; i++) {\n      currentStateList.add(createStateForPosition(i));\n    }\n  }\n\n  private ModelState createStateForPosition(int position) {\n    EpoxyModel<?> model = adapter.getCurrentModels().get(position);\n    model.addedToAdapter = true;\n    ModelState state = ModelState.build(model, position, immutableModels);\n\n    ModelState previousValue = currentStateMap.put(state.id, state);\n    if (previousValue != null) {\n      int previousPosition = previousValue.position;\n      EpoxyModel<?> previousModel = adapter.getCurrentModels().get(previousPosition);\n      throw new IllegalStateException(\"Two models have the same ID. ID's must be unique!\"\n          + \" Model at position \" + position + \": \" + model\n          + \" Model at position \" + previousPosition + \": \" + previousModel);\n    }\n\n    return state;\n  }\n\n  /**\n   * Find all removal operations and add them to the result list. The general strategy here is to\n   * walk through the {@link #oldStateList} and check for items that don't exist in the new list.\n   * Walking through it in order makes it easy to batch adjacent removals.\n   */\n  private void collectRemovals(UpdateOpHelper helper) {\n    for (ModelState state : oldStateList) {\n      // Update the position of the item to take into account previous removals,\n      // so that future operations will reference the correct position\n      state.position -= helper.getNumRemovals();\n","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/DiffHelper.java#L223-L259","documentation":"createStateForPosition builds a ModelState keyed by model id; if another model already occupies that id in currentStateMap, it throws IllegalStateException listing both positions and models. Epoxy requires globally unique model ids within a set of models because diffing and stable-id RecyclerView behavior key off them.","triggerScenarios":"Adding two models with the same id() to the adapter's model list — e.g. duplicate add(), a loop reusing one model instance/id, or forgetting to set a unique id on programmatically built models — then building state via prepareStateForDiff or observing an insert.","commonSituations":"Models created in a loop without setting distinct ids; accidentally adding the same model instance twice; two view types sharing a default/auto id; switching to diffing from an adapter that never enforced unique ids.","solutions":["Assign a unique id to every model (e.g. .id(position) or a stable business key)","Check for accidental duplicate add() of the same model instance","If a model legitimately appears twice, give each occurrence a distinct id or use EpoxyModel with id including a sub-identifier","Add a debug assert before setModels to detect duplicate ids early (EpoxyController.validateModelBuilding does this in controllers)"],"exampleFix":"// before\nfor (Item item : items) {\n  add(new ItemModel_().id(1).item(item)); // duplicate id\n}\n\n// after\nfor (Item item : items) {\n  add(new ItemModel_().id(item.getId()).item(item));\n}","handlingStrategy":"validation","validationCode":"// assert unique ids before building models\nSet<Long> seen = new HashSet<>();\nfor (EpoxyModel<?> m : models) {\n  if (!seen.add(m.id())) throw new IllegalStateException(\"Duplicate model id: \" + m.id());\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Assign a unique id to every model, derived from data (not a constant)","Enable Epoxy's model-validation in debug builds to catch duplicates early","Never add the same model instance twice; build fresh models per item"],"tags":["android","epoxy","duplicate-id","diffing"],"backgroundTag":"invalid-argument-value","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"}