{"record":{"id":"b0cbb7a7c0f6f2d2","repo":"apache/hadoop","slug":"permission-denied-is-not-allowed-to-change-quo","errorCode":null,"errorMessage":"Permission denied: {} is not allowed to change quota of {}","messagePattern":"Permission denied: (.+?) is not allowed to change quota of (.+?)","errorType":"exception","errorClass":"AccessControlException","httpStatus":null,"severity":"error","filePath":"hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/Quota.java","lineNumber":87,"sourceCode":"  }\n\n  /**\n   * Set quota for the federation path.\n   * @param path Federation path.\n   * @param namespaceQuota Name space quota.\n   * @param storagespaceQuota Storage space quota.\n   * @param type StorageType that the space quota is intended to be set on.\n   * @param checkMountEntry whether to check the path is a mount entry.\n   * @throws IOException If the quota system is disabled or if\n   * checkMountEntry is true and the path is a mount entry.\n   */\n  public void setQuota(String path, long namespaceQuota, long storagespaceQuota,\n      StorageType type, boolean checkMountEntry) throws IOException {\n    if (!router.isQuotaEnabled()) {\n      throw new IOException(\"The quota system is disabled in Router.\");\n    }\n    if (checkMountEntry && isMountEntry(path)) {\n      throw new AccessControlException(\n          \"Permission denied: \" + RouterRpcServer.getRemoteUser()\n              + \" is not allowed to change quota of \" + path);\n    }\n    setQuotaInternal(path, null, namespaceQuota, storagespaceQuota, type);\n  }\n\n  /**\n   * Set quota for the federation path.\n   * @param path Federation path.\n   * @param locations Locations of the Federation path.\n   * @param namespaceQuota Name space quota.\n   * @param storagespaceQuota Storage space quota.\n   * @param type StorageType that the space quota is intended to be set on.\n   * @throws IOException If the quota system is disabled.\n   */\n  void setQuotaInternal(String path, List<RemoteLocation> locations,\n      long namespaceQuota, long storagespaceQuota, StorageType type)\n      throws IOException {","sourceCodeStart":69,"sourceCodeEnd":105,"githubUrl":"https://github.com/apache/hadoop/blob/2add9630210752f88ceb1bb74eb65e37bf41da8e/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/Quota.java#L69-L105","documentation":"Quota.setQuota() throws AccessControlException when checkMountEntry is true and isMountEntry(path) confirms the path is itself a mount table entry. Quota for a mount point must be managed via the mount table (hdfs dsadmin commands / RouterAdmin updateMountTableEntry with quota), not via dfsadmin setQuota on the exact mount path, so the Router rejects the direct RPC.","triggerScenarios":"hdfs dfsadmin -setQuota <mount-path> executed through the Router where <mount-path> exactly matches a mount table source path (checkMountEntry=true comes from the setQuota RPC path used by RouterClientProtocol.setQuota); scripted quota tooling that applies quotas to federation roots.","commonSituations":"Operators migrating from single-cluster HDFS run dfsadmin setQuota on the federation root; automation that sets quotas on every directory including mount points; confusion between mount-level quota (stored in the mount table record) and NS/DS quota on the underlying namespace.","solutions":["To set quota on a mount point, update the mount entry itself: hdfs dsadmin -updateMount <src> <ns> <dest> -nsquota <n> -ssquota <s> (RouterAdmin set/quota in mount table)","To set quota on a normal subdirectory (not a mount entry), run dfsadmin -setQuota on a non-mount path under the mount","Audit scripts to skip paths that appear in the mount table (hdfs dsadmin -listMountTable)"],"exampleFix":"# before: rejected, /data is a mount entry\nhdfs dfsadmin -fs hdfs://router -setQuota 100000 /data\n\n# after: set quota via the mount table entry\nhdfs dsadmin -updateMount /data ns1 /data -nsquota 100000 -ssquota 1073741824","handlingStrategy":"validation","validationCode":"// Skip paths that are themselves mount entries before calling setQuota(checkMountEntry=true)\nMountTableResolver resolver = (MountTableResolver) router.getSubclusterResolver();\nif (resolver.getMountPoint(path) != null) {\n  throw new IllegalArgumentException(path + \" is a mount entry; update its quota via the mount table\");\n}\nquota.setQuota(path, nsQuota, ssQuota, type, true);","typeGuard":null,"tryCatchPattern":"try {\n  quota.setQuota(path, nq, sq, type, true);\n} catch (AccessControlException e) {\n  if (e.getMessage() != null && e.getMessage().contains(\"not allowed to change quota\")) {\n    // path is a mount entry: switch to mount-table quota update (hdfs dsadmin -updateMount ... -nsquota ...)\n  } else { throw e; }\n}","preventionTips":["Maintain the list of mount source paths and exclude them from dfsadmin setQuota automation","Use hdfs dsadmin mount commands for mount-level quota, dfsadmin setQuota only for subdirectories","Document for operators which paths are mount entries"],"tags":["hdfs","router-based-federation","quota","access-control","mount-table"],"backgroundTag":"permission-denied","analyzedSha":"2add9630210752f88ceb1bb74eb65e37bf41da8e","analyzedAt":"2026-08-22T19:55:07.957Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}