{"record":{"id":"93655fee611deec1","repo":"apache/hadoop","slug":"not-allow-to-remove-an-inner-node","errorCode":null,"errorMessage":"Not allow to remove an inner node: {}","messagePattern":"Not allow to remove an inner node: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/NetworkTopology.java","lineNumber":224,"sourceCode":"      if (!NodeBase.ROOT.equals(loc)) {\n        loc = loc.substring(1);\n      }\n      InnerNode rack = (InnerNode) clusterMap.getLoc(loc);\n      return (rack == null) ? new ArrayList<>(0)\n          : new ArrayList<>(rack.getChildren());\n    } finally {\n      netlock.readLock().unlock();\n    }\n  }\n\n  /** Remove a node\n   * Update node counter and rack counter if necessary\n   * @param node node to be removed; can be null\n   */ \n  public void remove(Node node) {\n    if (node==null) return;\n    if( node instanceof InnerNode ) {\n      throw new IllegalArgumentException(\n        \"Not allow to remove an inner node: \"+NodeBase.getPath(node));\n    }\n    LOG.info(\"Removing a node: \"+NodeBase.getPath(node));\n    netlock.writeLock().lock();\n    try {\n      if (clusterMap.remove(node)) {\n        InnerNode rack = (InnerNode)getNode(node.getNetworkLocation());\n        if (rack == null) {\n          numOfRacks--;\n        }\n        interRemoveNodeWithEmptyRack(node);\n      }\n      LOG.debug(\"NetworkTopology became:\\n{}\", this);\n    } finally {\n      netlock.writeLock().unlock();\n    }\n  }\n","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/net/NetworkTopology.java#L206-L242","documentation":"NetworkTopology.remove(Node) removes only leaf nodes. Inner nodes (racks/switches) are structural: their counters (numOfRacks, empty-rack bookkeeping) are updated by removing their leaves, so removing an InnerNode directly is rejected with IllegalArgumentException before the write lock is taken.","triggerScenarios":"Calling remove(node) where node instanceof InnerNode — e.g. a node obtained from getNode(path)/getDatanodesInRack() that is actually a rack, or a test stub extending InnerNode.","commonSituations":"Decommissioning or cluster-drain tooling that fetches nodes by path and removes them without a type check; unit tests with mock nodes extending InnerNodeImpl.","solutions":["Remove each leaf under the rack instead of the rack itself — the rack disappears automatically when its last leaf is removed","Filter nodes with !(node instanceof InnerNode) before calling remove()","To reset the whole map, rebuild the topology object rather than removing inner nodes"],"exampleFix":"// before\ntopology.remove(nodeFromLookup); // may be an InnerNode\n\n// after\nif (!(nodeFromLookup instanceof InnerNode)) {\n  topology.remove(nodeFromLookup);\n}","handlingStrategy":"validation","validationCode":"if (node instanceof InnerNode) {\n  throw new IllegalArgumentException(\n      \"Refusing to remove inner node \" + NodeBase.getPath(node));\n}\ntopology.remove(node);","typeGuard":"static boolean isRemovableLeaf(Node n) {\n  return n != null && !(n instanceof InnerNode);\n}","tryCatchPattern":null,"preventionTips":["Type-check every node obtained from path-based lookups before remove()","Model rack teardown as iteration over leaves, not removal of the rack object"],"tags":["hadoop-common","network-topology","invalid-argument","java"],"backgroundTag":"inner-node-rejected","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}