{"record":{"id":"f54c74fb8c080c91","repo":"elastic/elasticsearch","slug":"duplicate-overlapping-exclusive-paths-found-in-fil","errorCode":null,"errorMessage":"duplicate/overlapping exclusive paths found in files entitlements: {} and {}","messagePattern":"duplicate/overlapping exclusive paths found in files entitlements: (.+?) and (.+?)","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java","lineNumber":168,"sourceCode":"                                    + \"]\"\n                            );\n                        }\n                        exclusivePath.moduleNames.add(efe.moduleName());\n                    }\n                }\n            }\n        }\n        return exclusivePaths.values().stream().sorted(comparing(ExclusivePath::path, comparison.pathComparator())).distinct().toList();\n    }\n\n    static void validateExclusivePaths(List<ExclusivePath> exclusivePaths, FileAccessTreeComparison comparison) {\n        if (exclusivePaths.isEmpty() == false) {\n            ExclusivePath currentExclusivePath = exclusivePaths.get(0);\n            for (int i = 1; i < exclusivePaths.size(); ++i) {\n                ExclusivePath nextPath = exclusivePaths.get(i);\n                if (comparison.samePath(currentExclusivePath.path(), nextPath.path)\n                    || comparison.isParent(currentExclusivePath.path(), nextPath.path())) {\n                    throw new IllegalArgumentException(\n                        \"duplicate/overlapping exclusive paths found in files entitlements: \" + currentExclusivePath + \" and \" + nextPath\n                    );\n                }\n                currentExclusivePath = nextPath;\n            }\n        }\n    }\n\n    @SuppressForbidden(reason = \"we need the separator as a char, not a string\")\n    static char separatorChar() {\n        return File.separatorChar;\n    }\n\n    private static final Logger logger = LogManager.getLogger(FileAccessTree.class);\n    private static final String FILE_SEPARATOR = getDefaultFileSystem().getSeparator();\n    static final FileAccessTreeComparison DEFAULT_COMPARISON = Platform.LINUX.isCurrent()\n        ? new CaseSensitiveComparison(separatorChar())\n        : new CaseInsensitiveComparison(separatorChar());","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java#L150-L186","documentation":"Thrown by FileAccessTree.validateExclusivePaths when two exclusive paths are identical or one is a parent (ancestor) of another within the same sorted list. Even if the components are the same, nested or duplicated exclusive paths create ambiguity and are rejected. The check uses the comparison's path comparator and isParent/samePath predicates, so it is platform-aware (separator, case).","triggerScenarios":"After sorting exclusive paths, the loop finds a pair where comparison.samePath(prev, next) or comparison.isParent(prev, next) is true. Happens when a policy lists both a directory and a subdirectory as exclusive, or lists the same path twice.","commonSituations":"A policy YAML lists '/data' and '/data/logs' both as exclusive; copy-paste duplication of a path entry; relative vs absolute forms of the same path both present; Windows backslash vs forward-slash normalization edge cases.","solutions":["Merge the nested exclusive paths into the single broadest (parent) entry.","Remove the duplicate sub-path entry from the policy.","Run the paths through a normalizer and de-duplicate before declaring them exclusive."],"exampleFix":"// before\nfiles:\n  - { path: /var/lib/es/data, exclusive: true }\n  - { path: /var/lib/es/data/sub, exclusive: true }\n\n// after: keep only the parent\nfiles:\n  - { path: /var/lib/es/data, exclusive: true }","handlingStrategy":"validation","validationCode":"// Pre-validate: no exclusive path is a parent of another\nList<Path> sorted = paths.stream().sorted().toList();\nfor (int i = 1; i < sorted.size(); i++) {\n  if (sorted.get(i).startsWith(sorted.get(i - 1))) {\n    throw new IllegalArgumentException(\"overlapping exclusive paths: \" + sorted.get(i-1) + \" >= \" + sorted.get(i));\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Declare only the broadest directory as exclusive; drop nested sub-paths.","De-duplicate policy paths after normalization."],"tags":["entitlement","files","exclusive-access","policy","path-overlap"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}