elastic/elasticsearch · critical · IllegalStateException
jar hell! class: {clazz} jar1: {previous} jar2: {jarpath}
Error message
jar hell!
class: {clazz}
jar1: {previous}
jar2: {jarpath} What it means
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.
Source
Thrown at libs/core/src/main/java/org/elasticsearch/jdk/JarHell.java:308
}
Path previous = clazzes.put(clazz, jarpath);
if (previous != null) {
if (previous.equals(jarpath)) {
// throw a better exception in this ridiculous case.
// unfortunately the zip file format allows this buggy possibility
// UweSays: It can, but should be considered as bug :-)
throw new IllegalStateException(
"jar hell!"
+ System.lineSeparator()
+ "class: "
+ clazz
+ System.lineSeparator()
+ "exists multiple times in jar: "
+ jarpath
+ " !!!!!!!!!"
);
} else {
throw new IllegalStateException(
"jar hell!"
+ System.lineSeparator()
+ "class: "
+ clazz
+ System.lineSeparator()
+ "jar1: "
+ previous
+ System.lineSeparator()
+ "jar2: "
+ jarpath
);
}
}
}
private static URL toURL(URI uri) {
try {
return uri.toURL();View on GitHub (pinned to db6a809a66)
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').
Example fix
// before: both lib/foo-1.0.jar and modules/bar/foo-1.1.jar contain com.foo.Bar
// after: exclude the transitive duplicate in build.gradle
dependencies {
implementation('com.example:bar') {
exclude group: 'com.foo', module: 'foo'
}
} Defensive patterns
Strategy: validation
Validate before calling
// Before launch, scan the full classpath for cross-jar class collisions
import java.util.jar.*;
import java.util.*;
import java.nio.file.*;
void assertNoCrossJarDuplicates(List<Path> jars) throws IOException {
Map<String, Path> owners = new HashMap<>();
for (Path jar : jars) {
try (var jf = new JarFile(jar.toFile())) {
var e = jf.entries();
while (e.hasMoreElements()) {
String n = e.nextElement().getName();
if (!n.endsWith(".class")) continue;
Path prev = owners.put(n, jar);
if (prev != null && !prev.equals(jar)) {
throw new IllegalStateException(n + " in both " + prev + " and " + jar);
}
}
}
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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/.
Related errors
- jar hell! class: {clazz} exists multiple times in jar: {jarp
- Classpath should not contain empty elements! (outdated shell
- jar hell! duplicate jar [{element}] on classpath: {classPath
- jar hell! duplicate jar on classpath: {path}
- %s requires Java %s:, your system: %s
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/a414cbc79acf9bf6.
Report an issue: GitHub.