{"record":{"id":"0323155f940997aa","repo":"apache/hadoop","slug":"cannot-rename-within-internal-dirs-of-mount-table-032315","errorCode":null,"errorMessage":"Cannot Rename within internal dirs of mount table: dest={} is readOnly","messagePattern":"Cannot Rename within internal dirs of mount table: dest=(.+?) is readOnly","errorType":"exception","errorClass":"AccessControlException","httpStatus":null,"severity":"error","filePath":"hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ViewFs.java","lineNumber":603,"sourceCode":"          fsState.resolve(getUriPath(src), true);\n      if (resSrcWithLastComp.isInternalDir() || resSrcWithLastComp\n          .isLastInternalDirLink()) {\n        throw new AccessControlException(\n            \"Cannot Rename within internal dirs of mount table: src=\" + src\n                + \" is readOnly\");\n      } else {\n        // This is fallback and let's set the src fs with this fallback\n        resSrc = resSrcWithLastComp;\n      }\n    }\n\n    InodeTree.ResolveResult<AbstractFileSystem> resDst =\n        fsState.resolve(getUriPath(dst), false);\n\n    if (resDst.isInternalDir()) {\n      if (fsState.getRootFallbackLink() == null) {\n        // If fallback is null, we can't rename to dst.\n        throw new AccessControlException(\n            \"Cannot Rename within internal dirs of mount table: dest=\" + dst\n                + \" is readOnly\");\n      }\n      // if the fallback exist, we may have chance to rename to fallback path\n      // where dst parent is matching to internalDir.\n      InodeTree.ResolveResult<AbstractFileSystem> resDstWithLastComp =\n          fsState.resolve(getUriPath(dst), true);\n      if (resDstWithLastComp.isInternalDir()) {\n        // We need to get fallback here. If matching fallback path not exist, it\n        // will fail later. This is a very special case: Even though we are on\n        // internal directory, we should allow to rename, so that src files will\n        // moved under matching fallback dir.\n        resDst = new InodeTree.ResolveResult<AbstractFileSystem>(\n            InodeTree.ResultKind.INTERNAL_DIR,\n            fsState.getRootFallbackLink().getTargetFileSystem(), \"/\",\n            new Path(resDstWithLastComp.resolvedPath), false);\n      } else {\n        // The link resolved to some target fs or fallback fs.","sourceCodeStart":585,"sourceCodeEnd":621,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ViewFs.java#L585-L621","documentation":"The destination half of ViewFs.renameInternal: when dst resolves (with resolveLastComponent=false) to an internal dir and no root fallback link is configured, there is no target FileSystem to receive the rename, so it throws AccessControlException(\"Cannot Rename within internal dirs of mount table: dest=<dst> is readOnly\"). With a fallback configured, the code instead tries resolving dst with the last component to allow renames that land under the fallback (see comment in source).","triggerScenarios":"FileContext.rename(src, viewfs:///archive, ...) where /archive is a virtual container with links only beneath it and linkFallback is unset; tools archiving data to federated parent dirs that the mount table does not mount directly.","commonSituations":"Archive/rotation jobs writing to directory levels that exist only virtually; same federation clients as errors 993/994 — the src side was fixed via fallback but dst now fails, or vice versa.","solutions":["Configure fs.viewfs.mounttable.<n>.linkFallback so dest falls through to a real cluster","Add an explicit mount link for the destination parent (fs.viewfs.mounttable.<n>.link./archive)","Rename to a concrete path inside an existing mount point"],"exampleFix":"<!-- before: rename to /archive (virtual) throws -->\n\n<!-- after (core-site.xml) -->\n<property><name>fs.viewfs.mounttable.c1.link./archive</name>\n  <value>hdfs://nnArchive:8020/archive</value></property>","handlingStrategy":"try-catch","validationCode":"static boolean renameDstOk(ViewFileSystem vfs, Path dst) {\n  String s = dst.toUri().getPath();\n  if (s.equals(\"/\")) return false;\n  return vfs.getMountPoints().stream()\n      .map(mp -> mp.getMountedOnPath().toUri().getPath())\n      .anyMatch(m -> s.startsWith(m.endsWith(\"/\") ? m : m + \"/\")); // false -> needs linkFallback or a new link\n}","typeGuard":null,"tryCatchPattern":"try {\n  fc.rename(src, dst, Rename.NONE);\n} catch (AccessControlException ace) {\n  if (ace.getMessage().contains(\"dest=\") && ace.getMessage().contains(\"readOnly\")) {\n    // dest resolves to internal dir without fallback: add linkFallback or link the dest parent\n  }\n}","preventionTips":["Archive targets should be explicitly mounted parents, not virtual containers","Set linkFallback so destinations outside mount points still resolve"],"tags":["viewfs","mount-table","rename","access-control","hadoop-common"],"backgroundTag":"viewfs-internal-dir-protected","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}