{"record":{"id":"c3963fb731bbc4c8","repo":"elastic/elasticsearch","slug":"invalid-module-name-in-policy-layer-s-does-not","errorCode":null,"errorMessage":"Invalid module name in policy: layer [%s] does not have module [%s]; available modules [%s]; policy path [%s]","messagePattern":"Invalid module name in policy: layer \\[(.+?)\\] does not have module \\[(.+?)\\]; available modules \\[(.+?)\\]; policy path \\[(.+?)\\]","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyUtils.java","lineNumber":134,"sourceCode":"                    );\n                }\n            } catch (Exception e) {\n                throw new IllegalStateException(\"Unable to parse policy patch for layer [\" + layerName + \"]\", e);\n            }\n        }\n        return null;\n    }\n\n    static VersionedPolicy decodeEncodedPolicy(String base64String, String layerName, boolean isExternalPlugin) throws IOException {\n        byte[] policyDefinition = Base64.getDecoder().decode(base64String);\n        return new PolicyParser(new ByteArrayInputStream(policyDefinition), layerName, isExternalPlugin).parseVersionedPolicy();\n    }\n\n    private static void validatePolicyScopes(String layerName, Policy policy, Set<String> moduleNames, String policyLocation) {\n        // TODO: should this check actually be part of the parser?\n        for (Scope scope : policy.scopes()) {\n            if (moduleNames.contains(scope.moduleName()) == false) {\n                throw new IllegalStateException(\n                    Strings.format(\n                        \"Invalid module name in policy: layer [%s] does not have module [%s]; available modules [%s]; policy path [%s]\",\n                        layerName,\n                        scope.moduleName(),\n                        String.join(\", \", moduleNames),\n                        policyLocation\n                    )\n                );\n            }\n        }\n    }\n\n    public static Policy parsePolicyIfExists(String pluginName, Path pluginRoot, boolean isExternalPlugin) throws IOException {\n        Path policyFile = pluginRoot.resolve(POLICY_FILE_NAME);\n        if (Files.exists(policyFile)) {\n            try (var inputStream = Files.newInputStream(policyFile, StandardOpenOption.READ)) {\n                return new PolicyParser(inputStream, pluginName, isExternalPlugin).parsePolicy();\n            }","sourceCodeStart":116,"sourceCodeEnd":152,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyUtils.java#L116-L152","documentation":"Thrown by PolicyUtils.validatePolicyScopes while cross-checking a parsed policy's scopes against the set of module names known to belong to the layer. Every scope's moduleName must appear in moduleNames; otherwise the policy references a module the layer does not own and cannot enforce.","triggerScenarios":"An encoded policy patch lists a scope whose `module` field names a module that is not in the moduleNames set passed to parseEncodedPolicyIfExists. Common when a plugin declares scopes for a sibling module it does not contain.","commonSituations":"Typos in the module name in a policy file; renaming a module without updating its policy patch; copy-pasting a policy from one plugin into another whose module set differs; third-party plugin trying to grant entitlements to a core module it does not own.","solutions":["Read the message: it lists the offending module, the available modules, and the policy path.","Fix the scope's `module` field to match one of the listed available modules.","If the module was renamed, update the policy file to use the new name.","If you genuinely need to scope another module, the policy must be supplied by that module's layer, not yours."],"exampleFix":"// before\n{\n  \"module\": \"old-module-name\",\n  \"entitlements\": { ... }\n}\n\n// after\n{\n  \"module\": \"renamed-module\",\n  \"entitlements\": { ... }\n}","handlingStrategy":"validation","validationCode":"public static void validateScopesAgainstModules(Policy policy, Set<String> moduleNames) {\n    for (Scope s : policy.scopes()) {\n        if (!moduleNames.contains(s.moduleName())) {\n            throw new IllegalStateException(\"Scope module \" + s.moduleName()\n                + \" not in available modules \" + moduleNames);\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate policy files from the same manifest that declares module names so they cannot drift.","Run validatePolicyScopes in a build-time check before packaging a plugin.","When renaming a module, grep policy files for the old name in the same PR."],"tags":["entitlements","policy","config","modules"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}