elastic/elasticsearch · error · IllegalStateException
jar hell! duplicate jar [{element}] on classpath: {classPath
Error message
jar hell!
duplicate jar [{element}] on classpath: {classPath} What it means
Thrown as an IllegalStateException by JarHell.parseClassPath when a jar element (a path ending in .jar) resolves to a URL already seen earlier in the same classpath parse. Two different classpath strings that point to the same underlying jar file (after URL normalization) trip this check; the offending element and the full classpath are included. This is the classpath-parsing-level duplicate detection, distinct from the deeper duplicate-class scan in checkJarHell.
Source
Thrown at libs/core/src/main/java/org/elasticsearch/jdk/JarHell.java:154
if (element.startsWith("/") && "\\".equals(fileSeparator)) {
// "correct" the entry to become a normal entry
// change to correct file separators
element = element.replace("/", "\\");
// if there is a drive letter, nuke the leading separator
if (element.length() >= 3 && element.charAt(2) == ':') {
element = element.substring(1);
}
}
// now just parse as ordinary file
try {
if (element.equals("/")) {
// Eclipse adds this to the classpath when running unit tests...
continue;
}
URL url = PathUtils.get(element).toUri().toURL();
// junit4.childvm.count
if (urlElements.add(url) == false && element.endsWith(".jar")) {
throw new IllegalStateException(
"jar hell!" + System.lineSeparator() + "duplicate jar [" + element + "] on classpath: " + classPath
);
}
} catch (MalformedURLException e) {
// should not happen, as we use the filesystem API
throw new RuntimeException(e);
}
}
return Collections.unmodifiableSet(urlElements);
}
/**
* Returns a set of URLs that contain artifacts from both the non-JDK boot
* modules and class path. These URLs constitute the loadable application
* artifacts in the system class loader.
*/
public static Set<URL> parseModulesAndClassPath() {
return Stream.concat(parseClassPath().stream(), JarHell.nonJDKBootModuleURLs()).collect(toUnmodifiableSet());View on GitHub (pinned to db6a809a66)
Solutions
- De-duplicate the classpath: ensure each jar appears once (use `readlink -f` / `realpath` to canonicalize before comparing).
- If the duplicate is a symlink-vs-real conflict, list only the canonical path.
- Audit the bin/* script or wrapper that builds ES_CLASSPATH for double-appends.
- Run `echo $ES_CLASSPATH | tr ':' '\n' | sort | uniq -d` to find the duplicate.
Example fix
# before ES_CLASSPATH="/opt/es/lib/x.jar:/opt/es/lib/x.jar" # after ES_CLASSPATH="/opt/es/lib/x.jar"
Defensive patterns
Strategy: validation
Validate before calling
// Canonicalize and de-duplicate classpath entries before startup
String dedupClasspath(String cp) throws IOException {
String sep = System.getProperty("path.separator");
LinkedHashSet<String> seen = new LinkedHashSet<>();
for (String e : cp.split(Pattern.quote(sep))) {
if (e.isEmpty()) continue;
seen.add(Path.of(e).toRealPath().toString()); // canonicalize symlinks
}
return String.join(sep, seen);
} Type guard
static boolean classpathHasNoDuplicateJars(String cp) throws IOException {
String sep = System.getProperty("path.separator");
Set<Path> seen = new HashSet<>();
for (String e : cp.split(Pattern.quote(sep))) {
if (e.endsWith(".jar") && !seen.add(Path.of(e).toRealPath())) return false;
}
return true;
} Try / catch
// IllegalStateException surfaces at startup; dedupe the classpath before launch // rather than catching at runtime.
Prevention
- Canonicalize paths (realpath/readlink -f) before comparing jar entries.
- Don't append a jar that's already in ES_HOME/lib from plugin scripts.
- Audit IDE run configs and custom wrappers for duplicate entries.
- Use `echo $ES_CLASSPATH | tr ':' '\n' | sort | uniq -d` as a preflight check.
When it happens
Trigger: Listing the same jar twice in java.class.path via different path spellings that normalize to the same URL: e.g. '/opt/es/lib/x.jar:/opt/es/lib/x.jar', or a symlink plus its real path, or './x.jar' alongside an absolute form. Triggered at line 153 when urlElements.add returns false and the element ends with .jar.
Common situations: A plugin install script appending a jar that's already on the base classpath. Custom launch wrappers concatenating ES_HOME/lib/* with an explicit jar list. Symlinks causing the same physical jar to appear under two paths. A misconfigured IDE run config listing a dependency twice.
Related errors
- jar hell! duplicate jar on classpath: {path}
- Classpath should not contain empty elements! (outdated shell
- jar hell! class: {clazz} exists multiple times in jar: {jarp
- jar hell! class: {clazz} jar1: {previous} jar2: {jarpath}
- unknown scheme:{rootURI.getScheme()}
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/40ffd6b78c612b69.
Report an issue: GitHub.