{"record":{"id":"0de8c933d9ebc124","repo":"apache/hadoop","slug":"srciip-getpath-can-t-be-moved-from-encryptio","errorCode":null,"errorMessage":"srcIIP.getPath() + \" can't be moved from encryption zone \" + srcEZPath + \" to encryption zone \" + dstEZPath + \".\"","messagePattern":"srcIIP\\.getPath\\(\\) \\+ \" can't be moved from encryption zone \" \\+ srcEZPath \\+ \" to encryption zone \" \\+ dstEZPath \\+ \"\\.\"","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EncryptionZoneManager.java","lineNumber":507,"sourceCode":"        getParentEncryptionZoneForPath(dstIIP);\n    final boolean srcInEZ = (srcParentEZI != null);\n    final boolean dstInEZ = (dstParentEZI != null);\n    if (srcInEZ && !dstInEZ) {\n      throw new IOException(\n          srcIIP.getPath() + \" can't be moved from an encryption zone.\");\n    } else if (dstInEZ && !srcInEZ) {\n      throw new IOException(\n          srcIIP.getPath() + \" can't be moved into an encryption zone.\");\n    }\n\n    if (srcInEZ) {\n      if (!srcParentEZI.equals(dstParentEZI)) {\n        final String srcEZPath = getFullPathName(srcParentEZI.getINodeId());\n        final String dstEZPath = getFullPathName(dstParentEZI.getINodeId());\n        final StringBuilder sb = new StringBuilder(srcIIP.getPath());\n        sb.append(\" can't be moved from encryption zone \").append(srcEZPath)\n            .append(\" to encryption zone \").append(dstEZPath).append(\".\");\n        throw new IOException(sb.toString());\n      }\n      checkMoveValidityForReencryption(srcIIP.getPath(),\n          srcParentEZI.getINodeId());\n    } else if (dstInEZ) {\n      checkMoveValidityForReencryption(dstIIP.getPath(),\n          dstParentEZI.getINodeId());\n    }\n  }\n\n  private void checkMoveValidityForReencryption(final String pathName,\n      final long zoneId) throws IOException {\n    assert dir.hasReadLock();\n    final ZoneReencryptionStatus zs = reencryptionStatus.getZoneStatus(zoneId);\n    if (zs != null && zs.getState() != ZoneReencryptionStatus.State.Completed) {\n      final StringBuilder sb = new StringBuilder(pathName);\n      sb.append(\" can't be moved because encryption zone \");\n      sb.append(getFullPathName(zoneId));\n      sb.append(\" is currently under re-encryption\");","sourceCodeStart":489,"sourceCodeEnd":525,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EncryptionZoneManager.java#L489-L525","documentation":"IOException from EncryptionZoneManager.checkMoveValidity: both source and destination are inside encryption zones, but not the SAME zone (srcParentEZI != dstParentEZI via getFullPathName comparison). Each zone keys its files with its own EDEK/key name, so a rename between zones cannot re-key data and is rejected.","triggerScenarios":"hdfs dfs -mv from /zone-a/file to /zone-b/... where /zone-a and /zone-b were created with hdfs crypto -createZone under different paths (even when both use the same KMS key name -- zone identity, not key name, is what matters).","commonSituations":"Per-team or per-project encryption zones with a pipeline trying to hand files across; reorganizations that 'move' data between zones; assuming a shared key name makes zones interchangeable.","solutions":["Copy-then-delete across zones: hdfs dfs -cp <src> <dst> && hdfs dfs -rm <src> -- the client decrypts with zone A's key and re-encrypts under zone B on write.","For large cross-zone migrations use hadoop distcp (KMS-aware copy+delete).","Redesign so collaborating paths share one zone (create the zone at the common ancestor before data is written; zones need empty dirs)."],"exampleFix":"# before: fails -- different zones, even with the same key name\nhdfs dfs -mv /zone-a/data.parquet /zone-b/data.parquet\n# after: decrypt+re-encrypt via copy, then delete source\nhdfs dfs -cp /zone-a/data.parquet /zone-b/data.parquet\nhdfs dfs -rm /zone-a/data.parquet","handlingStrategy":"validation","validationCode":"EncryptionZone s = dfs.getEncryptionZoneForPath(src);\nEncryptionZone d = dfs.getEncryptionZoneForPath(dst);\nboolean same = s != null && d != null\n    && s.getPath().equals(d.getPath());   // same zone => rename allowed\nif (s != null && d != null && !same) {\n  // cross-zone rename rejected: copy-then-delete (decrypt + re-encrypt) instead\n}","typeGuard":"static boolean isEncryptionZoneMoveViolation(IOException e) {\n  return e.getMessage() != null && e.getMessage().contains(\"encryption zone\");\n}","tryCatchPattern":"try {\n  fs.rename(src, dst);\n} catch (RemoteException re) {\n  IOException e = re.unwrapRemoteException(IOException.class);\n  if (e.getMessage() != null && e.getMessage().contains(\"from encryption zone\")) {\n    copyThenDelete(src, dst);   // client decrypts with zone A key, writes encrypted under zone B\n  } else { throw e; }\n}","preventionTips":["Compare zone identity (not key name) of src and dst before every cross-directory mv.","Plan zone layout at provisioning time: one zone per workflow that must exchange files.","Use distcp for large cross-zone migrations; it handles KMS auth and re-encryption."],"tags":["hdfs","encryption-zone","rename","key-management","distcp"],"backgroundTag":"rename-across-encryption-zone","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}