{"record":{"id":"a414cbc79acf9bf6","repo":"elastic/elasticsearch","slug":"jar-hell-class-clazz-jar1-previous-jar2-j","errorCode":null,"errorMessage":"jar hell!\nclass: {clazz}\njar1: {previous}\njar2: {jarpath}","messagePattern":"jar hell!\nclass: (.+?)\njar1: (.+?)\njar2: (.+?)","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"critical","filePath":"libs/core/src/main/java/org/elasticsearch/jdk/JarHell.java","lineNumber":308,"sourceCode":"        }\n        Path previous = clazzes.put(clazz, jarpath);\n        if (previous != null) {\n            if (previous.equals(jarpath)) {\n                // throw a better exception in this ridiculous case.\n                // unfortunately the zip file format allows this buggy possibility\n                // UweSays: It can, but should be considered as bug :-)\n                throw new IllegalStateException(\n                    \"jar hell!\"\n                        + System.lineSeparator()\n                        + \"class: \"\n                        + clazz\n                        + System.lineSeparator()\n                        + \"exists multiple times in jar: \"\n                        + jarpath\n                        + \" !!!!!!!!!\"\n                );\n            } else {\n                throw new IllegalStateException(\n                    \"jar hell!\"\n                        + System.lineSeparator()\n                        + \"class: \"\n                        + clazz\n                        + System.lineSeparator()\n                        + \"jar1: \"\n                        + previous\n                        + System.lineSeparator()\n                        + \"jar2: \"\n                        + jarpath\n                );\n            }\n        }\n    }\n\n    private static URL toURL(URI uri) {\n        try {\n            return uri.toURL();","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/core/src/main/java/org/elasticsearch/jdk/JarHell.java#L290-L326","documentation":"Thrown by JarHell.checkClass when the same fully-qualified class name is present in two DIFFERENT jars on the classpath (previous jar != current jarpath). This is the classic 'jar hell' condition: the JVM could load either copy, so Elasticsearch refuses to start. The message lists both conflicting jar paths.","triggerScenarios":"Two distinct jars on the classpath each contain a class with the identical fully-qualified name. checkClass() records the first jar per class name; when the same class name is encountered in a second jar, previous.equals(jarpath) is false so this else-branch fires, naming jar1 and jar2.","commonSituations":"A transitive dependency pulled in two versions of the same library; a plugin bundles a library already shipped by ES core; a shaded jar conflicts with the original; copy-pasting a dependency jar into lib/ that already lives in modules/.","solutions":["Identify which two jars from the message ship the named class and remove or exclude one of them.","Run 'jar tf <jar> | grep <clazz>' on both jars to confirm the duplication.","Use Gradle's dependencyInsight or 'gradle dependencies' to find the conflicting transitive pull and add an exclude rule.","If it is a plugin, rebuild it without bundling the duplicate library (mark the dep as 'provided' or 'compileOnly')."],"exampleFix":"// before: both lib/foo-1.0.jar and modules/bar/foo-1.1.jar contain com.foo.Bar\n\n// after: exclude the transitive duplicate in build.gradle\ndependencies {\n  implementation('com.example:bar') {\n    exclude group: 'com.foo', module: 'foo'\n  }\n}","handlingStrategy":"validation","validationCode":"// Before launch, scan the full classpath for cross-jar class collisions\nimport java.util.jar.*;\nimport java.util.*;\nimport java.nio.file.*;\n\nvoid assertNoCrossJarDuplicates(List<Path> jars) throws IOException {\n  Map<String, Path> owners = new HashMap<>();\n  for (Path jar : jars) {\n    try (var jf = new JarFile(jar.toFile())) {\n      var e = jf.entries();\n      while (e.hasMoreElements()) {\n        String n = e.nextElement().getName();\n        if (!n.endsWith(\".class\")) continue;\n        Path prev = owners.put(n, jar);\n        if (prev != null && !prev.equals(jar)) {\n          throw new IllegalStateException(n + \" in both \" + prev + \" and \" + jar);\n        }\n      }\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Mark dependencies that ES already ships as 'compileOnly'/'provided' in plugins.","Run 'gradle dependencies' to detect version conflicts before packaging.","Use shading with relocation when a plugin must bundle a conflicting library."],"tags":["jar-hell","duplicate-class","classpath","dependency-conflict","startup"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}