{"record":{"id":"6a56be573ce79a5f","repo":"apache/hadoop","slug":"the-new-recovery-id-must-be-greater-than-the-c","errorCode":null,"errorMessage":"The new recovery id: {} must be greater than the current one: {}","messagePattern":"The new recovery id: (.+?) must be greater than the current one: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/ReplicaUnderRecovery.java","lineNumber":68,"sourceCode":"   * @param from where to copy from\n   */\n  public ReplicaUnderRecovery(ReplicaUnderRecovery from) {\n    super(from);\n    this.original = (LocalReplica) from.getOriginalReplica();\n    this.recoveryId = from.getRecoveryID();\n  }\n\n  @Override\n  public long getRecoveryID() {\n    return recoveryId;\n  }\n\n  @Override\n  public void setRecoveryID(long recoveryId) {\n    if (recoveryId > this.recoveryId) {\n      this.recoveryId = recoveryId;\n    } else {\n      throw new IllegalArgumentException(\"The new recovery id: \" + recoveryId\n          + \" must be greater than the current one: \" + this.recoveryId);\n    }\n  }\n\n  /**\n   * Get the original replica that's under recovery\n   * @return the original replica under recovery\n   */\n  @Override\n  public ReplicaInfo getOriginalReplica() {\n    return original;\n  }\n  \n  @Override //ReplicaInfo\n  public ReplicaState getState() {\n    return ReplicaState.RUR;\n  }\n  ","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/ReplicaUnderRecovery.java#L50-L86","documentation":"ReplicaUnderRecovery.setRecoveryID(long) enforces strictly increasing recovery ids: the recoveryId doubles as the generation stamp the replica will be bumped to after recovery, so a new recovery attempt must carry a larger id than the current one. Passing an id less than or equal to the current one throws IllegalArgumentException showing both values.","triggerScenarios":"DataNode.updateReplicaUnderRecovery()/sync blocks call setRecoveryID(newId) when a later recovery attempt (higher requested block GS) arrives for a replica already under recovery, and newId <= the stored recoveryId. Happens with a stale NameNode/primary retried recovery request, an old client resending a recovery command, or clock/sequence regressions in test-generated recovery ids.","commonSituations":"Replayed or out-of-order block-recovery RPCs from an HA NameNode that lagged behind (standby resending an old recovery task); tests that reuse the same recoveryId across iterations; a retry of the same recovery attempt instead of a fresh, higher one.","solutions":["Ensure each new recovery attempt uses a recoveryId strictly greater than the previous (normally the new requested block generation stamp)","Treat this exception as a signal the recovery request is stale: log and skip rather than crash","For NameNode HA: verify the active NN is the one issuing recovery so stale standbys cannot replay old ids"],"exampleFix":"// before\nrur.setRecoveryID(newRecoveryId); // throws if newRecoveryId <= rur.getRecoveryID()\n\n// after\nif (newRecoveryId > rur.getRecoveryID()) {\n  rur.setRecoveryID(newRecoveryId);\n} else {\n  LOG.warn(\"Stale recovery id {} for {} - keeping {}\",\n      newRecoveryId, rur, rur.getRecoveryID());\n}","handlingStrategy":"validation","validationCode":"void bumpRecoveryId(ReplicaUnderRecovery r, long newId) {\n  if (newId > r.getRecoveryID()) {\n    r.setRecoveryID(newId);\n  } else {\n    LOG.debug(\"Ignoring stale recovery id {} <= {}\", newId, r.getRecoveryID());\n  }\n}","typeGuard":null,"tryCatchPattern":"try { rur.setRecoveryID(newId); } catch (IllegalArgumentException e) { /* stale recovery request: drop it, do not crash the DN thread */ LOG.warn(\"Stale recovery request: {}\", e.getMessage()); }","preventionTips":["Treat recovery ids as monotonic - derive them from the requested new block generation stamp","On retry storms, deduplicate recovery RPCs by (blockId, recoveryId) before applying"],"tags":["hdfs","datanode","block-recovery","generation-stamp","replica","illegal-argument"],"backgroundTag":"stale-generation-stamp","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}