{"record":{"id":"66879404c5eead1e","repo":"apache/hadoop","slug":"changing-storage-type-is-not-allowed","errorCode":null,"errorMessage":"Changing storage type is not allowed.","messagePattern":"Changing storage type is not allowed\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DataNode.java","lineNumber":1183,"sourceCode":"      existingStorageLocations.put(loc.getNormalizedUri().toString(), loc);\n    }\n\n    ChangedVolumes results = new ChangedVolumes();\n    results.newLocations.addAll(newStorageLocations);\n\n    for (Iterator<Storage.StorageDirectory> it = storage.dirIterator();\n         it.hasNext(); ) {\n      Storage.StorageDirectory dir = it.next();\n      boolean found = false;\n      for (Iterator<StorageLocation> newLocationItr =\n           results.newLocations.iterator(); newLocationItr.hasNext();) {\n        StorageLocation newLocation = newLocationItr.next();\n        if (newLocation.matchesStorageDirectory(dir)) {\n          StorageLocation oldLocation = existingStorageLocations.get(\n              newLocation.getNormalizedUri().toString());\n          if (oldLocation != null &&\n              oldLocation.getStorageType() != newLocation.getStorageType()) {\n            throw new IOException(\"Changing storage type is not allowed.\");\n          }\n          // Update the unchanged locations as this location\n          // from the new conf is really not a new one.\n          newLocationItr.remove();\n          results.unchangedLocations.add(newLocation);\n          found = true;\n          break;\n        }\n      }\n\n      // New conf doesn't have the storage location which available in\n      // the current storage locations. Add to the deactivateLocations list.\n      if (!found) {\n        LOG.info(\"Deactivation request received for active volume: {}\",\n            dir.getRoot());\n        results.deactivateLocations.add(\n            StorageLocation.parse(dir.getRoot().toString()));\n      }","sourceCodeStart":1165,"sourceCodeEnd":1201,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DataNode.java#L1165-L1201","documentation":"During refreshVolumes, every new location that matches an existing StorageDirectory is compared against the previously configured location for the same normalized URI; if the storage type differs (oldLocation.getStorageType() != newLocation.getStorageType()), HDFS throws IOException(\"Changing storage type is not allowed.\") because a live volume cannot be retyped in place. No volumes are modified when this fires.","triggerScenarios":"Live-reconfiguring dfs.datanode.data.dir where an existing path's bracketed type prefix changes: [DISK]file:///data/dn1 -> [SSD]file:///data/dn1, or dropping the prefix entirely (default DISK) where the old config had [ARCHIVE]/[SSD], or vice versa.","commonSituations":"Tiering a hot volume to SSD/NVMe; unifying per-host configs where one host had explicit type prefixes and the shared template omits them; typos in type names that silently become DISK and collide with an old typed entry.","solutions":["Split into two reconfigs: first submit the value WITHOUT the location (it deactivates), then re-add it with the new type prefix in a second reconfig","Or stop the DataNode, update dfs.datanode.data.dir, and restart — the startup path re-reads types cleanly","Double-check every bracketed prefix; a missing prefix means DISK"],"exampleFix":"# before (single reconfig, rejected)\ndfs.datanode.data.dir=[SSD]file:///data/dn1,[DISK]file:///data/dn2\n# after step 1: drop dn1\ndfs.datanode.data.dir=[DISK]file:///data/dn2\n# after step 2: re-add with the new type\ndfs.datanode.data.dir=[SSD]file:///data/dn1,[DISK]file:///data/dn2","handlingStrategy":"validation","validationCode":"Map<String, StorageType> oldByUri = new HashMap<>();\nfor (StorageLocation l : DataNode.getStorageLocations(currentConf))\n  oldByUri.put(l.getNormalizedUri().toString(), l.getStorageType());\nfor (StorageLocation l : newLocations) {\n  StorageType t = oldByUri.get(l.getNormalizedUri().toString());\n  if (t != null && t != l.getStorageType())\n    throw new IllegalArgumentException(\"cannot retype \" + l + \" from \" + t);\n}","typeGuard":null,"tryCatchPattern":"catch (ReconfigurationException e) {\n  if (String.valueOf(e.getCause()).contains(\"Changing storage type\")) {\n    // split into remove-then-re-add with the new type, in two separate reconfigs\n  }\n}","preventionTips":["Type prefixes are part of a volume's identity — never edit them in place in the reconfig value","Plan type migrations as remove-then-readd, with data re-replication in between if needed","Diff old vs new dfs.datanode.data.dir per normalized URI before every volume refresh"],"tags":["hadoop","hdfs","datanode","volume-management","storage-type","reconfiguration"],"backgroundTag":"storage-type-mismatch","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}