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

  1. Identify which two jars from the message ship the named class and remove or exclude one of them.
  2. Run 'jar tf <jar> | grep <clazz>' on both jars to confirm the duplication.
  3. Use Gradle's dependencyInsight or 'gradle dependencies' to find the conflicting transitive pull and add an exclude rule.
  4. 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

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


AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12). Data as JSON: /api/errors/a414cbc79acf9bf6. Report an issue: GitHub.