{"record":{"id":"3eecec299589064e","repo":"apache/hadoop","slug":"srciip-getpath-can-t-be-moved-from-an-encryp","errorCode":null,"errorMessage":"srcIIP.getPath() + \" can't be moved from an encryption zone.\"","messagePattern":"srcIIP\\.getPath\\(\\) \\+ \" can't be moved from an encryption zone\\.\"","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":493,"sourceCode":"   *\n   * @param srcIIP source IIP\n   * @param dstIIP destination IIP\n   * @throws IOException if the src cannot be renamed to the dst\n   */\n  void checkMoveValidity(INodesInPath srcIIP, INodesInPath dstIIP)\n      throws IOException {\n    assert dir.hasReadLock();\n    if (!hasCreatedEncryptionZone()) {\n      return;\n    }\n    final EncryptionZoneInt srcParentEZI =\n        getParentEncryptionZoneForPath(srcIIP);\n    final EncryptionZoneInt dstParentEZI =\n        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) {","sourceCodeStart":475,"sourceCodeEnd":511,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/EncryptionZoneManager.java#L475-L511","documentation":"IOException from EncryptionZoneManager.checkMoveValidity (reached via FSDirRenameOp): a rename was requested where the source's parent is inside an encryption zone but the destination is not. HDFS rejects renames out of an encryption zone because the file's EDEK belongs to the zone's key -- a rename would carry ciphertext outside the zone's key context and silently break the zone's guarantees, so no cross-boundary rename is allowed.","triggerScenarios":"hdfs dfs -mv (or FileSystem.rename) from /zone/src/file to a path whose nearest EZ ancestor is null: e.g., moving encrypted working data to a shared scratch area, temp dirs, or a landing zone outside the EZ.","commonSituations":"ETL pipelines that stage inside an encryption zone and 'move' results to a general-purpose directory; users trying to relocate data to quota-free or differently-governed paths; scripts written before the EZ existed.","solutions":["Replace the rename with copy-then-delete: hdfs dfs -cp <src> <dst> && hdfs dfs -rm <src> -- the client decrypts on read and writes plaintext at the destination.","For large moves use hadoop distcp (it also performs copy+delete and handles KMS auth).","Restructure layout so the whole workflow lives inside one encryption zone (zones must be created on empty directories, so plan this up front)."],"exampleFix":"# before: fails\nhdfs dfs -mv /secure/data.csv /scratch/data.csv\n# after: copy (client decrypts), then remove the source\nhdfs dfs -cp /secure/data.csv /scratch/data.csv\nhdfs dfs -rm /secure/data.csv","handlingStrategy":"validation","validationCode":"EncryptionZone s = dfs.getEncryptionZoneForPath(src);\nEncryptionZone d = dfs.getEncryptionZoneForPath(dst);\nboolean srcIn = s != null, dstIn = d != null;\nif (srcIn && !dstIn) {\n  // rename OUT of a zone is rejected: plan copy-then-delete 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(\"can't be moved\")) {\n    copyThenDelete(src, dst);   // cp decrypts to plaintext outside the zone, then rm\n  } else { throw e; }\n}","preventionTips":["Before any mv across directories, compare hdfs crypto zone membership of src and dst (getEncryptionZoneForPath).","Design pipelines to stay inside a single zone; treat cross-zone moves as copies.","Wrap move logic in a helper that falls back to copy+delete when zones differ."],"tags":["hdfs","encryption-zone","rename","security"],"backgroundTag":"rename-across-encryption-zone","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}