{"record":{"id":"aee5b8af6d7478d3","repo":"airbnb/epoxy","slug":"already-paired","errorCode":null,"errorMessage":"Already paired.","messagePattern":"Already paired\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"epoxy-adapter/src/main/java/com/airbnb/epoxy/ModelState.java","lineNumber":50,"sourceCode":"    state.id = model.id();\n    state.position = position;\n\n    if (immutableModel) {\n      state.model = model;\n    } else {\n      state.hashCode = model.hashCode();\n    }\n\n    return state;\n  }\n\n  /**\n   * Used for an item inserted into the new list when we need to track moves that effect the\n   * inserted item in the old list.\n   */\n  void pairWithSelf() {\n    if (pair != null) {\n      throw new IllegalStateException(\"Already paired.\");\n    }\n\n    pair = new ModelState();\n    pair.lastMoveOp = 0;\n    pair.id = id;\n    pair.position = position;\n    pair.hashCode = hashCode;\n    pair.pair = this;\n    pair.model = model;\n  }\n\n  @Override\n  public String toString() {\n    return \"ModelState{\"\n        + \"id=\" + id\n        + \", model=\" + model\n        + \", hashCode=\" + hashCode\n        + \", position=\" + position","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/airbnb/epoxy/blob/e45bd3a61fe3a1f130e184f5b8dcf172ab99025a/epoxy-adapter/src/main/java/com/airbnb/epoxy/ModelState.java#L32-L68","documentation":"ModelState.pairWithSelf creates a paired ModelState used during diffing to track moves of newly inserted items. It throws IllegalStateException if the model already has a pair, since re-pairing would corrupt the diff algorithm's invariants.","triggerScenarios":"pairWithSelf() invoked on a ModelState whose `pair` field is already non-null — a diff-internals invariant violation, typically from a corrupted or concurrently-modified model list during notifyModelStateChanges.","commonSituations":"Concurrent modification of the model list while the diff is running; internal Epoxy diff bugs; calling internal ModelState APIs from app code.","solutions":["Ensure all model list mutations happen on the main/single UI thread","Rebuild the model list (setModels) instead of mutating ModelState directly","If reproducible, upgrade Epoxy — this can indicate a diffing bug","Never call pairWithSelf from application code"],"exampleFix":"// before\nnew Thread(() -> controller.setModels(list)).start();\n// after\nrunOnUiThread(() -> controller.setModels(list));","handlingStrategy":"validation","validationCode":"synchronized (controller) { controller.setModels(newList); }","typeGuard":null,"tryCatchPattern":"try { diffHelper.notifyModelStateChanges(); } catch (IllegalStateException e) { controller.requestModelBuild(); }","preventionTips":["Mutate the model list only on the main thread","Never mutate ModelState or call pairWithSelf directly","Prefer full setModels rebuilds over incremental internal diffing","Upgrade Epoxy if reproducible with current versions"],"tags":["android","epoxy","diffing","internal-state"],"backgroundTag":"internal-invariant-violation","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"}