{"record":{"id":"cd88bee52335ec27","repo":"elastic/elasticsearch","slug":"invalid-relative-directory-valid-values-con","errorCode":null,"errorMessage":"invalid relative directory: {}, valid values: [config, data, home]","messagePattern":"invalid relative directory: (.+?), valid values: \\[config, data, home\\]","errorType":"validation","errorClass":"PolicyValidationException","httpStatus":null,"severity":"error","filePath":"libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java","lineNumber":189,"sourceCode":"        } else if (platform.equals(\"macos\")) {\n            return Platform.MACOS;\n        } else if (platform.equals(\"windows\")) {\n            return Platform.WINDOWS;\n        } else {\n            throw new PolicyValidationException(\"invalid platform: \" + platform + \", valid values: [linux, macos, windows]\");\n        }\n    }\n\n    private static BaseDir parseBaseDir(String baseDir) {\n        return switch (baseDir) {\n            case \"config\" -> BaseDir.CONFIG;\n            case \"data\" -> BaseDir.DATA;\n            case \"home\" -> BaseDir.USER_HOME;\n            case \"shared_data\" -> BaseDir.SHARED_DATA;\n            // it would be nice to limit this to just ES modules, but we don't have a way to plumb that through to here\n            // however, we still don't document in the error case below that shared_repo and shared_data is valid\n            case \"shared_repo\" -> BaseDir.SHARED_REPO;\n            default -> throw new PolicyValidationException(\n                \"invalid relative directory: \" + baseDir + \", valid values: [config, data, home]\"\n            );\n        };\n    }\n\n    @ExternalEntitlement(parameterNames = { \"paths\" }, esModulesOnly = false)\n    @SuppressWarnings(\"unchecked\")\n    public static FilesEntitlement build(List<Object> paths) {\n        if (paths == null || paths.isEmpty()) {\n            throw new PolicyValidationException(\"must specify at least one path\");\n        }\n        BiFunction<Map<String, Object>, String, String> checkString = (values, key) -> {\n            Object value = values.remove(key);\n            if (value == null) {\n                return null;\n            } else if (value instanceof String str) {\n                return str;\n            }","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java#L171-L207","documentation":"Thrown by FilesEntitlement.parseBaseDir when the `relative_to` (or `basedir_if_relative`) value is not one of the recognised base-directory literals. Note: the switch actually accepts five values (config, data, home, shared_data, shared_repo) but the error message only documents three (config, data, home) by design — the shared_* dirs are intentionally undocumented because they are intended for Elasticsearch-internal modules only.","triggerScenarios":"A files-entitlement entry sets `relative_to` or `basedir_if_relative` to an unrecognised literal (e.g. \"logs\", \"tmp\", \"base\", \"root\", or a typo like \"conf\").","commonSituations":"Guessing a directory name; using a path-like value (\"/etc/elasticsearch\") instead of the symbolic literal; external plugin author trying to use shared_data/shared_repo (which is reserved for ES modules); typo in the literal.","solutions":["For external plugins, use one of the documented values: \"config\", \"data\", or \"home\".","If you are an Elasticsearch-internal module, \"shared_data\" and \"shared_repo\" are also accepted but intentionally undocumented in the error.","Do not put a literal filesystem path in this field — it is a symbolic base-dir selector."],"exampleFix":"// before\n{ \"relative_path\": \"logs\", \"relative_to\": \"/var/lib/es\", \"mode\": \"read_write\" }\n\n// after\n{ \"relative_path\": \"logs\", \"relative_to\": \"data\", \"mode\": \"read_write\" }","handlingStrategy":"validation","validationCode":"private static final Set<String> PUBLIC_BASE_DIRS = Set.of(\"config\", \"data\", \"home\");\nprivate static final Set<String> INTERNAL_BASE_DIRS = Set.of(\"shared_data\", \"shared_repo\");\npublic static void validateBaseDir(String dir, boolean isExternalPlugin) {\n    if (!PUBLIC_BASE_DIRS.contains(dir) && (!INTERNAL_BASE_DIRS.contains(dir) || isExternalPlugin)) {\n        throw new IllegalArgumentException(\"invalid base dir: \" + dir);\n    }\n}","typeGuard":"public static boolean isValidPublicBaseDir(String d) {\n    return Set.of(\"config\",\"data\",\"home\").contains(d);\n}","tryCatchPattern":null,"preventionTips":["Use only config/data/home in external plugins.","Remember the value is a symbolic selector, never a filesystem path.","If you reach for shared_data/shared_repo as an external plugin, redesign — those are reserved."],"tags":["entitlements","files","policy","validation","paths"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T12:17:08.281Z"}