{"record":{"id":"7851411367605f2d","repo":"juicedata/juicefs","slug":"invalid-acl-multiple-entries-with-same-scope-typ","errorCode":null,"errorMessage":"Invalid ACL: multiple entries with same scope, type and name.","messagePattern":"Invalid ACL: multiple entries with same scope, type and name\\.","errorType":"validation","errorClass":"AclException","httpStatus":null,"severity":"error","filePath":"sdk/java/src/main/java/io/juicefs/utils/AclTransformation.java","lineNumber":169,"sourceCode":"\n  private AclTransformation() {\n  }\n\n  public static final Comparator<AclEntry> ACL_ENTRY_COMPARATOR = new Comparator<AclEntry>() {\n    @Override\n    public int compare(AclEntry entry1, AclEntry entry2) {\n      return ComparisonChain.start().compare(entry1.getScope(), entry2.getScope(), Ordering.explicit(ACCESS, DEFAULT)).compare(entry1.getType(), entry2.getType(), Ordering.explicit(USER, GROUP, MASK, OTHER)).compare(entry1.getName(), entry2.getName(), Ordering.natural().nullsFirst()).result();\n    }\n  };\n\n  public static List<AclEntry> buildAndValidateAcl(ArrayList<AclEntry> aclBuilder) throws AclException {\n    aclBuilder.trimToSize();\n    Collections.sort(aclBuilder, ACL_ENTRY_COMPARATOR);\n    // Full iteration to check for duplicates and invalid named entries.\n    AclEntry prevEntry = null;\n    for (AclEntry entry : aclBuilder) {\n      if (prevEntry != null && ACL_ENTRY_COMPARATOR.compare(prevEntry, entry) == 0) {\n        throw new AclException(\"Invalid ACL: multiple entries with same scope, type and name.\");\n      }\n      if (entry.getName() != null && (entry.getType() == MASK || entry.getType() == OTHER)) {\n        throw new AclException(\"Invalid ACL: this entry type must not have a name: \" + entry + \".\");\n      }\n      prevEntry = entry;\n    }\n\n    ScopedAclEntries scopedEntries = new ScopedAclEntries(aclBuilder);\n    checkMaxEntries(scopedEntries);\n\n    // Search for the required base access entries.  If there is a default ACL,\n    // then do the same check on the default entries.\n    for (AclEntryType type : EnumSet.of(USER, GROUP, OTHER)) {\n      AclEntry accessEntryKey = new AclEntry.Builder().setScope(ACCESS).setType(type).build();\n      if (Collections.binarySearch(scopedEntries.getAccessEntries(), accessEntryKey, ACL_ENTRY_COMPARATOR) < 0) {\n        throw new AclException(\"Invalid ACL: the user, group and other entries are required.\");\n      }\n      if (!scopedEntries.getDefaultEntries().isEmpty()) {","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/juicedata/juicefs/blob/c9a67b23e8e08ec23ec331aa6f1675e2319e921c/sdk/java/src/main/java/io/juicefs/utils/AclTransformation.java#L151-L187","documentation":"AclTransformation.buildAndValidateAcl throws AclException when, after sorting, two ACL entries compare equal (same scope, type and name) — duplicates are not representable in a valid ACL. Called by filterAclEntriesByAclSpec, mergeAclEntries and replaceAclEntries.","triggerScenarios":"Submitting an AclSpec (via setAcl/modifyAcl paths) containing two entries that normalize to the same scope+type+name, e.g. duplicate named users or two default user entries.","commonSituations":"Programmatic AclSpec construction with repeated addEntry for the same principal; merging ACLs where input already contains duplicates; copy-paste of ACL entries in tooling.","solutions":["Deduplicate the AclSpec before applying (use a Set keyed by scope/type/name)","Use replaceAclEntries with a clean full ACL list instead of merging overlapping specs","Inspect the current ACL (getAclStatus) and remove the duplicate entry","Fix the tooling/script that generates the spec to avoid repeats"],"exampleFix":"// before\nSet<AclEntry> entries = new HashSet<>(); // preserves last-wins on name\n// after\nMap<String, AclEntry> byKey = new LinkedHashMap<>();\nspec.forEach(e -> byKey.put(e.getScope()+\":\"+e.getType()+\":\"+e.getName(), e));\nAclSpec deduped = new AclSpec(new ArrayList<>(byKey.values()));","handlingStrategy":"validation","validationCode":"Set<String> seen = new HashSet<>();\nfor (AclEntry e : aclSpec.getEntries()) {\n  String k = e.getScope()+\":\"+e.getType()+\":\"+e.getName();\n  if (!seen.add(k)) throw new IllegalArgumentException(\"Duplicate ACL entry: \" + k);\n}","typeGuard":"boolean isDuplicateFree(List<AclEntry> entries) {\n  Set<String> keys = new HashSet<>();\n  for (AclEntry e : entries)\n    if (!keys.add(e.getScope()+\":\"+e.getType()+\":\"+e.getName())) return false;\n  return true;\n}","tryCatchPattern":"try {\n  AclTransformation.mergeAclEntries(...);\n} catch (AclException e) {\n  if (e.getMessage().contains(\"multiple entries with same scope, type and name\")) {\n    LOG.error(\"Deduplicate the AclSpec before applying\");\n  } else throw e;\n}","preventionTips":["Build AclSpecs with Sets/Maps keyed by scope:type:name","Read current ACL and merge carefully to avoid repeats","Unit-test spec generators for duplicate output"],"tags":["acl","validation","hadoop","permissions"],"backgroundTag":"schema-validation-failed","analyzedSha":"c9a67b23e8e08ec23ec331aa6f1675e2319e921c","analyzedAt":"2026-09-06T17:55:48.476Z","contentChangedAt":"2026-09-06T17:55:48.476Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}