{"record":{"id":"e99f635636ea272d","repo":"apache/hadoop","slug":"invalid-plan-hash","errorCode":"INVALID_PLAN_HASH","errorMessage":"Invalid or mis-matched hash.","messagePattern":"Invalid or mis-matched hash\\.","errorType":"exception","errorClass":"DiskBalancerException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DiskBalancer.java","lineNumber":457,"sourceCode":"   * @param planID - SHA-1 Hex Bytes\n   * @param plan   - Plan String\n   * @throws DiskBalancerException\n   */\n  private NodePlan verifyPlanHash(String planID, String plan)\n      throws DiskBalancerException {\n    final long sha1Length = 40;\n    if (plan == null || plan.length() == 0) {\n      LOG.error(\"Disk Balancer -  Invalid plan.\");\n      throw new DiskBalancerException(\"Invalid plan.\",\n          DiskBalancerException.Result.INVALID_PLAN);\n    }\n\n    if ((planID == null) ||\n        (planID.length() != sha1Length) ||\n        !DigestUtils.sha1Hex(plan.getBytes(StandardCharsets.UTF_8))\n            .equalsIgnoreCase(planID)) {\n      LOG.error(\"Disk Balancer - Invalid plan hash.\");\n      throw new DiskBalancerException(\"Invalid or mis-matched hash.\",\n          DiskBalancerException.Result.INVALID_PLAN_HASH);\n    }\n\n    try {\n      return NodePlan.parseJson(plan);\n    } catch (IOException ex) {\n      throw new DiskBalancerException(\"Parsing plan failed.\", ex,\n          DiskBalancerException.Result.MALFORMED_PLAN);\n    }\n  }\n\n  /**\n   * Verifies that this plan is not older than 24 hours.\n   *\n   * @param plan - Node Plan\n   */\n  private void verifyTimeStamp(NodePlan plan) throws DiskBalancerException {\n    long now = Time.now();","sourceCodeStart":439,"sourceCodeEnd":475,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DiskBalancer.java#L439-L475","documentation":"DiskBalancerException with Result.INVALID_PLAN_HASH from DiskBalancer.verifyPlanHash: the planID (expected to be the 40-hex-char SHA-1 of the plan bytes) is null, not exactly 40 characters, or does not match DigestUtils.sha1Hex(plan) computed on the DataNode. This integrity check guarantees the executed plan is byte-for-byte the one the planner signed, preventing corrupted or tampered plans from moving data.","triggerScenarios":"'hdfs diskbalancer -execute <planfile>' where the planID argument/plan-file hash differs from SHA-1 of the plan content: file corrupted or edited after generation (whitespace/line-ending changes), wrong hash copied from another plan, truncated file transfer, or planID typoed/truncated on the command line or API call.","commonSituations":"Editing a generated plan JSON (even reformatting) then executing it; copying plan files through tools that alter encoding/line endings; passing the planID of a different node's plan; programmatic submitPlan computing the hash over a different string than the plan payload sent.","solutions":["Regenerate the plan with 'hdfs diskbalancer -plan <node>' and execute the untouched file - the CLI passes the matching hash automatically","If the plan was edited intentionally, recompute and use the new SHA-1: sha1sum <planfile> (hex, lowercase) and pass that as the planID","Verify the file arrived intact: compare sha1sum on the machine that generated and the one executing the plan","For API callers, ensure planID = DigestUtils.sha1Hex(planData.getBytes(StandardCharsets.UTF_8)) of the exact string submitted"],"exampleFix":"# before: plan JSON edited/reformatted -> INVALID_PLAN_HASH\nvim /system/diskbalancer/node-plan.json   # pretty-printed, hash now stale\nhdfs diskbalancer -execute /system/diskbalancer/node-plan.json\n\n# after: regenerate (or recompute hash) so SHA-1 matches the bytes\nhdfs diskbalancer -plan <datanode>          # fresh file + matching hash\n# or: sha1sum /system/diskbalancer/node-plan.json  -> use that digest as planID","handlingStrategy":"validation","validationCode":"// Compute and verify the SHA-1 exactly like the DataNode does, before submit\nString planData = new String(Files.readAllBytes(Paths.get(planFile)),\n    StandardCharsets.UTF_8);\nString expected = DigestUtils.sha1Hex(planData.getBytes(StandardCharsets.UTF_8));\nif (planId == null || planId.length() != 40 || !expected.equalsIgnoreCase(planId)) {\n  throw new IllegalArgumentException(\n      \"planID must be the 40-hex SHA-1 of the plan bytes; computed: \" + expected);\n}\ndiskBalancer.submitPlan(planId, planVersion, planFile, planData, force);","typeGuard":null,"tryCatchPattern":"try {\n  diskBalancer.submitPlan(planId, planVersion, planFile, planData, force);\n} catch (DiskBalancerException e) {\n  if (e.getResult() == DiskBalancerException.Result.INVALID_PLAN_HASH) {\n    // plan bytes and ID diverge: either regenerate plan+ID together, or recompute\n    // sha1Hex(planData) and resubmit with that as planID - never hand-edit plans\n  }\n}","preventionTips":["Never edit/reformat a generated plan file; regenerate it instead so hash and bytes stay paired","Transfer plan files byte-exact (binary mode, checksum after copy: sha1sum on both ends)","When calling submitPlan programmatically, always derive planID via DigestUtils.sha1Hex(planData.getBytes(UTF_8)) from the exact payload you submit"],"tags":["hdfs","diskbalancer","sha1","integrity-check","plan-hash"],"backgroundTag":"checksum-mismatch","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}