{"record":{"id":"9151ba52487b3b8d","repo":"elastic/elasticsearch","slug":"failed-to-list-entitlement-jars-in-dir","errorCode":null,"errorMessage":"Failed to list entitlement jars in: ${dir}","messagePattern":"Failed to list entitlement jars in: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java","lineNumber":174,"sourceCode":"            return Stream.of(\"-XX:+UnlockDiagnosticVMOptions\", \"-XX:G1NumCollectionsKeepPinned=10000000\");\n        }\n        return Stream.of();\n    }\n\n    private static Stream<String> attachEntitlementAgent(Path esHome) {\n        Path dir = esHome.resolve(\"lib/entitlement-bridge\");\n        if (Files.exists(dir) == false) {\n            throw new IllegalStateException(\"Directory for entitlement bridge jar does not exist: \" + dir);\n        }\n        String bridgeJar;\n        try (var s = Files.list(dir)) {\n            var candidates = s.limit(2).toList();\n            if (candidates.size() != 1) {\n                throw new IllegalStateException(\"Expected one jar in \" + dir + \"; found \" + candidates.size());\n            }\n            bridgeJar = candidates.get(0).toString();\n        } catch (IOException e) {\n            throw new IllegalStateException(\"Failed to list entitlement jars in: \" + dir, e);\n        }\n\n        // We instrument classes in these modules to call the bridge. Because the bridge gets patched\n        // into java.base, we must export the bridge from java.base to these modules, as a comma-separated list\n        String modulesContainingEntitlementInstrumentation =\n            \"java.logging,java.net.http,java.naming,jdk.net,jdk.zipfs,jdk.management.agent\";\n        return Stream.of(\n            \"-XX:+EnableDynamicAgentLoading\",\n            \"-Djdk.attach.allowAttachSelf=true\",\n            \"--patch-module=java.base=\" + bridgeJar,\n            \"--add-exports=java.base/org.elasticsearch.entitlement.bridge=org.elasticsearch.entitlement,\"\n                + modulesContainingEntitlementInstrumentation\n        );\n    }\n}\n","sourceCodeStart":156,"sourceCodeEnd":190,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/distribution/tools/server-cli/src/main/java/org/elasticsearch/server/cli/SystemJvmOptions.java#L156-L190","documentation":"Thrown by SystemJvmOptions.attachEntitlementAgent when `Files.list(dir)` raises an IOException while enumerating the entitlement-bridge directory. The original IOException is wrapped in an IllegalStateException with the directory path as context. Unlike the existence and count checks, this is an OS-level I/O failure: the directory may exist but be unreadable due to permissions, race conditions (directory removed mid-listing), or filesystem errors (NFS stale handle, disk failure).","triggerScenarios":"Running Elasticsearch as a user without read permission on lib/entitlement-bridge. A concurrent process deletes the directory between the exists check and the list call. NFS or container overlay filesystem returning EIO. SELinux/AppArmor denying directory read.","commonSituations":"Wrong ownership on the distribution files (e.g. extracted as root, run as elasticsearch user without chown). Container securityContext using readOnlyRootFilesystem without granting read to lib. NFS mount glitches.","solutions":["Check permissions: `ls -la $ES_HOME/lib/entitlement-bridge/` as the elasticsearch user.","Fix ownership: `chown -R elasticsearch:elasticsearch $ES_HOME`.","Inspect dmesg / OS logs for filesystem errors; on NFS, remount or use a local volume.","On SELinux systems, run `restorecon -R $ES_HOME` and check audit logs for denials."],"exampleFix":"# before\n$ id; ls $ES_HOME/lib/entitlement-bridge/\nls: cannot open directory: Permission denied\n# after\nsudo chown -R elasticsearch:elasticsearch $ES_HOME\nsudo -u elasticsearch ls $ES_HOME/lib/entitlement-bridge/","handlingStrategy":"try-catch","validationCode":"Path bridge = esHome.resolve(\"lib/entitlement-bridge\");\nif (!Files.isReadable(bridge)) {\n    throw new IllegalStateException(\"Cannot read \" + bridge + \"; check ownership and SELinux/AppArmor.\");\n}","typeGuard":"static boolean isReadableDir(Path p) {\n    return Files.isDirectory(p) && Files.isReadable(p);\n}","tryCatchPattern":"try (var s = Files.list(bridge)) {\n    // ...\n} catch (IOException e) {\n    // surface the directory path, check perms, log the OS errno, and rethrow as IllegalStateException\n    throw new IllegalStateException(\"Failed to list entitlement jars in: \" + bridge, e);\n}","preventionTips":["Run `chown -R elasticsearch:elasticsearch $ES_HOME` after extraction.","On SELinux hosts, `restorecon -R $ES_HOME` and check `audit.log`.","Validate directory readability as part of a preflight script before launch."],"tags":["cli","entitlements","filesystem","permissions","io","agent"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}