elastic/elasticsearch · error · IllegalStateException
Classpath should not contain empty elements! (outdated shell
Error message
Classpath should not contain empty elements! (outdated shell script from a previous version?) classpath='{classPath}' What it means
Thrown as an IllegalStateException by JarHell.parseClassPath when the java.class.path system property, split on the path separator, yields one or more empty elements (e.g. consecutive separators like 'a.jar::b.jar' or a leading/trailing separator). Empty classpath elements technically behave like CWD, but Elasticsearch treats this as a misconfiguration — usually stale shell scripts from a prior version — and refuses to start. The full offending classpath string is included.
Source
Thrown at libs/core/src/main/java/org/elasticsearch/jdk/JarHell.java:125
}
String pathSeparator = System.getProperty("path.separator");
String fileSeparator = System.getProperty("file.separator");
String elements[] = classPath.split(pathSeparator);
Set<URL> urlElements = new LinkedHashSet<>(); // order is already lost, but some filesystems have it
for (String element : elements) {
/*
* Technically empty classpath element behaves like CWD.
* So below is the "correct" code, however in practice with ES, this is usually just a misconfiguration,
* from old shell scripts left behind or something:
*
* if (element.isEmpty()) {
* element = System.getProperty("user.dir");
* }
*
* Instead we just throw an exception, and keep it clean.
*/
if (element.isEmpty()) {
throw new IllegalStateException(
"Classpath should not contain empty elements! (outdated shell script from a previous"
+ " version?) classpath='"
+ classPath
+ "'"
);
}
// we should be able to just Paths.get() each element, but unfortunately this is not the
// whole story on how classpath parsing works: if you want to know, start at sun.misc.Launcher,
// be sure to stop before you tear out your eyes. we just handle the "alternative" filename
// specification which java seems to allow, explicitly, right here...
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);
}View on GitHub (pinned to db6a809a66)
Solutions
- Inspect the classpath string echoed in the message; remove the duplicated/leading/trailing separator.
- If building the classpath from variables, filter out empty entries before joining: `printf '%s:' "${arr[@]}" | sed 's/:$//'`.
- Replace stale bin/* shell scripts with the version shipping in the current distribution.
- Check ES_JAVA_OPTS / ES_CLASSPATH in the environment for stray separators.
Example fix
# before ES_CLASSPATH="lib/*::plugins/*" # double colon # after ES_CLASSPATH="lib/*:plugins/*"
Defensive patterns
Strategy: validation
Validate before calling
// De-duplicate and strip empty classpath elements before JVM startup
String cleanClasspath(String cp) {
String sep = System.getProperty("path.separator");
return Arrays.stream(cp.split(Pattern.quote(sep)))
.filter(s -> !s.isEmpty())
.distinct()
.collect(Collectors.joining(sep));
} Type guard
static boolean classpathHasNoEmptyElements(String cp) {
String sep = System.getProperty("path.separator");
return Arrays.stream(cp.split(Pattern.quote(sep), -1)).noneMatch(String::isEmpty);
} Try / catch
// IllegalStateException is thrown at startup; the fix is configuration, not catch. // Validate the classpath string before passing it to the JVM.
Prevention
- Build classpath strings by joining non-empty entries with a single separator.
- When concatenating optional variables, skip them when empty.
- Replace stale bin/* shell scripts when upgrading Elasticsearch.
- Run `echo $ES_CLASSPATH | tr ':' '\n' | grep -q '^$'` as a preflight check.
When it happens
Trigger: Setting ES_CLASSPATH or java.class.path with a duplicated separator (`lib/*::plugins/*`), a trailing colon, or a leading colon. Passing `-cp :foo.jar` or `-cp foo.jar:`. An env-var built by concatenating entries with an extra separator. An old bin/elasticsearch shell script from a previous ES version.
Common situations: Upgrading Elasticsearch and leaving old shell wrappers that append an empty classpath element. A shell variable that was empty and got concatenated into the classpath. A packaging script that joins paths with `${VAR}:` unconditionally. Custom plugin startup scripts.
Related errors
- jar hell! duplicate jar [{element}] on classpath: {classPath
- jar hell! duplicate jar on classpath: {path}
- jar hell! class: {clazz} exists multiple times in jar: {jarp
- jar hell! class: {clazz} jar1: {previous} jar2: {jarpath}
- Native-image classpath is empty
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/ca7ab6a7ca8b4609.
Report an issue: GitHub.