{"record":{"id":"2134a06f5bb0dfdc","repo":"apache/hadoop","slug":"invalid-move","errorCode":"INVALID_MOVE","errorMessage":"Disk Balancer - Source and destination volumes are same: {}","messagePattern":"Disk Balancer - Source and destination volumes are same: (.+?)","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":609,"sourceCode":"          blockMover.setRunnable();\n          blockMover.copyBlocks(entry.getKey(), entry.getValue());\n        }\n      }\n    });\n  }\n\n  /**\n   * Insert work items to work map.\n   * @param volumePair - VolumePair\n   * @param step - Move Step\n   */\n  private void createWorkPlan(final VolumePair volumePair, Step step)\n      throws DiskBalancerException {\n    if (volumePair.getSourceVolUuid().equals(volumePair.getDestVolUuid())) {\n      final String errMsg = \"Disk Balancer - Source and destination volumes \" +\n          \"are same: \" + volumePair.getSourceVolUuid();\n      LOG.warn(errMsg);\n      throw new DiskBalancerException(errMsg,\n          DiskBalancerException.Result.INVALID_MOVE);\n    }\n    long bytesToMove = step.getBytesToMove();\n    // In case we have a plan with more than\n    // one line of same VolumePair\n    // we compress that into one work order.\n    if (workMap.containsKey(volumePair)) {\n      bytesToMove += workMap.get(volumePair).getBytesToCopy();\n    }\n\n    DiskBalancerWorkItem work = new DiskBalancerWorkItem(bytesToMove, 0);\n\n    // all these values can be zero, if so we will use\n    // values from configuration.\n    work.setBandwidth(step.getBandwidth());\n    work.setTolerancePercent(step.getTolerancePercent());\n    work.setMaxDiskErrors(step.getMaxDiskErrors());\n    workMap.put(volumePair, work);","sourceCodeStart":591,"sourceCodeEnd":627,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DiskBalancer.java#L591-L627","documentation":"createWorkPlan(VolumePair, Step) rejects a plan step whose source volume UUID equals its destination volume UUID with Result.INVALID_MOVE. Copying data from a volume to itself is a no-op, so such a step means the plan is degenerate — usually planner output on an unusual volume layout or a hand-crafted plan. Note it is logged at WARN but still aborts submitPlan.","triggerScenarios":"A NodePlan step whose sourceVolumeUUID and destinationVolumeUUID are identical: emitted by the planner when one volume is somehow selected as both most- and least-used, or written manually into the plan JSON.","commonSituations":"Single-volume or perfectly balanced nodes producing degenerate planner output; hand-edited plans where a volume UUID was pasted into both sides; planner edge cases on tiny or unusual volume sets.","solutions":["Regenerate the plan; a fresh planner run on the current volume report usually will not emit a self-referencing step.","Inspect the plan JSON, remove or fix steps where both sides share a UUID, and recompute the planID (sha1 of the plan bytes) since editing changes the hash.","If the planner reproducibly emits same-volume steps for this node, attach the node volume report and file a Hadoop JIRA."],"exampleFix":"// before: submit plan that contains a same-volume step\ndiskBalancer.submitPlan(planID, version, planString, false);\n\n// after: filter degenerate steps client-side before submitting\nNodePlan plan = NodePlan.parseJson(planString);\nplan.setVolumeSetPlans(plan.getVolumeSetPlans().stream()\n    .filter(s -> !s.getSourceVolume().getUuid()\n        .equals(s.getDestinationVolume().getUuid()))\n    .collect(Collectors.toList()));\nString filtered = plan.toJson();\nString newPlanId = DigestUtils.sha1Hex(filtered.getBytes(StandardCharsets.UTF_8));\ndiskBalancer.submitPlan(newPlanId, version, filtered, false);","handlingStrategy":"validation","validationCode":"for (Step s : NodePlan.parseJson(planString).getVolumeSetPlans()) {\n  if (s.getSourceVolume().getUuid().equals(s.getDestinationVolume().getUuid())) {\n    // degenerate step: reject or filter the plan before submitting\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  diskBalancer.submitPlan(planID, version, plan, false);\n} catch (DiskBalancerException e) {\n  if (e.getResult() == DiskBalancerException.Result.INVALID_MOVE) {\n    // plan contains a self-move: filter the step, recompute planID hash, resubmit\n  }\n}","preventionTips":["Sanity-check generated plans for same-volume steps before execution","Remember any plan edit invalidates the planID — recompute sha1Hex after changes","Regenerate rather than patch plans when the planner output looks degenerate"],"tags":["disk-balancer","invalid-move","plan-validation","hdfs"],"backgroundTag":"invalid-plan-step","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}