elastic/elasticsearch · error · InvalidModuleDescriptorException

{name} found in top-level directory (unnamed package not all

Error message

{name} found in top-level directory (unnamed package not allowed in module)

What it means

Thrown as an InvalidModuleDescriptorException by EmbeddedModulePath.toPackageName(Path, String) when a .class file (other than module-info.class) is found at the top-level directory of a module's jar — i.e. it has no parent directory and thus belongs to the unnamed package. The Java Platform explicitly forbids the unnamed package in modules, so this is a fatal descriptor error during automatic-module scanning.

Source

Thrown at libs/core/src/main/java/org/elasticsearch/core/internal/provider/EmbeddedModulePath.java:218

    // Drops comments and trims the given string.
    private static String dropCommentAndTrim(String line) {
        int ci = line.indexOf('#');
        if (ci >= 0) {
            line = line.substring(0, ci);
        }
        return line.trim();
    }

    // Returns an optional containing the package name from a given path and separator, or an
    // empty optional if none.
    static Optional<String> toPackageName(Path file, String separator) {
        Path parent = file.getParent();
        if (parent == null) {
            String name = file.toString();
            if (name.endsWith(".class") && name.equals(MODULE_INFO) == false) {
                String msg = name + " found in top-level directory (unnamed package not allowed in module)";
                throw new InvalidModuleDescriptorException(msg);
            }
            return Optional.empty();
        }

        String pn = parent.toString().replace(separator, ".");
        if (isPackageName(pn)) {
            return Optional.of(pn);
        } else {
            // not a valid package name
            return Optional.empty();
        }
    }

    // Returns an optional containing the package name from a given binary class path name, or an
    // empty optional if none.
    static Optional<String> toPackageName(String name, String separator) {
        assert name.endsWith(separator) == false;
        int index = name.lastIndexOf(separator);

View on GitHub (pinned to db6a809a66)

Solutions

  1. Move the offending .class file into a directory matching its package declaration; never place classes at the jar root.
  2. If the class genuinely has no package, delete it or rewrite it to declare a package before packaging.
  3. Rebuild the jar with proper structure (Gradle/Maven jar tasks preserve package dirs by default — investigate any custom shading).
  4. Inspect the jar: `jar tf x.jar | grep -E '^[^/]+\.class$'` to find top-level class files.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-scan a jar for top-level class files before module-mode loading
boolean hasNoTopLevelClasses(Path jar) throws IOException {
    try (JarFile j = new JarFile(jar.toFile())) {
        return j.stream().noneMatch(e -> e.getName().endsWith(".class")
            && !e.getName().equals("module-info.class")
            && !e.getName().contains("/"));
    }
}

Type guard

static boolean isModuleSafeJar(Path jar) throws IOException {
    try (JarFile j = new JarFile(jar.toFile())) {
        return j.stream().filter(e -> e.getName().endsWith(".class")).allMatch(e -> e.getName().contains("/") || e.getName().equals("module-info.class"));
    }
}

Try / catch

try {
    return EmbeddedModulePath.descriptorFor(path);
} catch (InvalidModuleDescriptorException e) {
    if (e.getMessage().contains("top-level directory")) {
        // repack the jar with proper package directories; identify the entry via the message
        throw new RuntimeException("jar has unnamed-package class: " + path, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An embedded jar that will become a module contains a top-level .class file (e.g. 'Foo.class' at the jar root, not under any package directory) other than module-info.class. The scan hits toPackageName, parent is null, and the check at line 216 fires.

Common situations: Shading classes into a jar without preserving package directories. A compiled class whose package declaration was removed/empty (legacy code in the default package). Bundling a script-generated .class at the root. A bad zip/jar assembly step that flattens directories.

Related errors


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