elastic/elasticsearch · error · IllegalStateException
Unable to parse policy patch for layer [{}]
Error message
Unable to parse policy patch for layer [{}] What it means
Wrapped exception from PolicyUtils.parseEncodedPolicyIfExists. It tries to base64-decode and JSON-parse a policy patch supplied (typically via a system property / agent arg) for a named layer; any failure during decode, parse, or scope validation is caught and rethrown as an IllegalStateException with this message and the original cause attached.
Source
Thrown at libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyUtils.java:119
try {
var versionedPolicy = decodeEncodedPolicy(encodedPolicy, layerName, externalPlugin);
validatePolicyScopes(layerName, versionedPolicy.policy(), moduleNames, "<patch>");
// Empty versions defaults to "any"
if (versionedPolicy.versions().isEmpty() || versionedPolicy.versions().contains(version)) {
logger.info("Using policy patch for layer [{}]", layerName);
return versionedPolicy.policy();
} else {
logger.warn(
"Found a policy patch with version mismatch. The patch will not be applied. "
+ "Layer [{}]; policy versions [{}]; current version [{}]",
layerName,
String.join(",", versionedPolicy.versions()),
version
);
}
} catch (Exception e) {
throw new IllegalStateException("Unable to parse policy patch for layer [" + layerName + "]", e);
}
}
return null;
}
static VersionedPolicy decodeEncodedPolicy(String base64String, String layerName, boolean isExternalPlugin) throws IOException {
byte[] policyDefinition = Base64.getDecoder().decode(base64String);
return new PolicyParser(new ByteArrayInputStream(policyDefinition), layerName, isExternalPlugin).parseVersionedPolicy();
}
private static void validatePolicyScopes(String layerName, Policy policy, Set<String> moduleNames, String policyLocation) {
// TODO: should this check actually be part of the parser?
for (Scope scope : policy.scopes()) {
if (moduleNames.contains(scope.moduleName()) == false) {
throw new IllegalStateException(
Strings.format(
"Invalid module name in policy: layer [%s] does not have module [%s]; available modules [%s]; policy path [%s]",
layerName,View on GitHub (pinned to db6a809a66)
Solutions
- Inspect the caused-by exception (the original IOException or PolicyParserException) for the real parse failure.
- Regenerate the base64 policy payload from the canonical JSON (base64 encode without line wrapping) and ensure the JSON parses standalone.
- If the failure is scope validation, confirm every module referenced in the policy exists in the layer's available module set.
- Verify the encoded string has no trailing whitespace or newline when injected via system property.
Example fix
// before -Des.entitlements.policy_patch.mymodule=$(cat broken.json | base64) // after -Des.entitlements.policy_patch.mymodule=$(jq -c . valid-policy.json | base64 -w0)
Defensive patterns
Strategy: try-catch
Validate before calling
// Validate the base64 + JSON before injecting as a system property.
public static String encodePolicyPatch(Path policyJson) throws IOException {
byte[] json = Files.readAllBytes(policyJson);
// parse to confirm it's valid JSON / valid policy shape
try (var in = new ByteArrayInputStream(json)) {
new PolicyParser(in, "precheck", false).parsePolicy();
}
return Base64.getEncoder().encodeToString(json);
} Try / catch
try {
Policy p = PolicyUtils.parseEncodedPolicyIfExists(encoded, version, external, layerName, moduleNames);
} catch (IllegalStateException e) {
// message: "Unable to parse policy patch for layer [...]"
Throwable cause = e.getCause();
log.error("policy patch for {} failed: {}", layerName, cause == null ? e : cause);
// fall back to no patch (null) or fail fast depending on operator policy
} Prevention
- Always pre-parse the policy JSON before base64-encoding it.
- Generate the base64 with `base64 -w0` (no line wrapping) when injecting via system property.
- Keep the caused-by exception in logs — it has the real parse error.
- Pin the policy `versions` field to your current ES version to avoid silent skips.
When it happens
Trigger: A non-null encodedPolicy string is supplied to parseEncodedPolicyIfExists but it is not valid base64, or its decoded JSON is not a valid policy, or scope validation against moduleNames fails. The layer name in the message identifies which plugin/module layer caused it.
Common situations: Setting an entitlement policy patch via JVM system property with a malformed base64 payload; shipping a plugin whose embedded policy JSON has a syntax error; version mismatch where decode succeeds but downstream policy validation throws; copy-pasting a truncated base64 blob.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid module name in policy: layer [%s] does not have modu
- entitlement class [{}] has more than one constructor annotat
- entitlement class [{}] has non-static method annotated with
- entitlement class [{}] has more than one constructor and/or
- invalid mode: {}, valid values: [read, read_write]
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/8e1e2f7ef52f07ca.
Report an issue: GitHub.