elastic/elasticsearch · error · IllegalArgumentException

unexpected jar name: {jarName}

Error message

unexpected jar name: {jarName}

What it means

Thrown as an IllegalArgumentException by EmbeddedModulePath.version when the supplied jar file name does not end with '.jar'. version() derives an automatic module's version from the jar filename per the JDK ModuleFinder contract; a non-jar name breaks the parsing precondition. Because the method is called with path.getFileName() during automatic-module descriptor building, this typically means a misnamed embedded artifact.

Source

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

    // Scans a given path for class files and services.
    static ScanResult scan(Path path) throws IOException {
        try (var stream = Files.walk(path)) {
            Map<Boolean, Set<String>> map = stream.filter(p -> Files.isDirectory(p) == false)
                .map(p -> path.relativize(p).toString())
                .filter(p -> (p.endsWith(".class") ^ p.startsWith(SERVICES_PREFIX)))
                .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX), Collectors.toSet()));
            return new ScanResult(map.get(Boolean.FALSE), map.get(Boolean.TRUE));
        }
    }

    private static final Pattern DASH_VERSION = Pattern.compile("-(\\d+(\\.|$))");

    // Determines the module version (of an automatic module), given the jar file name. As per,
    // https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/module/ModuleFinder.html#of(java.nio.file.Path...)
    static Optional<ModuleDescriptor.Version> version(String jarName) {
        if (jarName.endsWith(".jar") == false) {
            throw new IllegalArgumentException("unexpected jar name: " + jarName);
        }
        // drop ".jar"
        String name = jarName.substring(0, jarName.length() - 4);
        // find first occurrence of -${NUMBER}. or -${NUMBER}$
        Matcher matcher = DASH_VERSION.matcher(name);
        if (matcher.find()) {
            int start = matcher.start();
            try {
                String tail = name.substring(start + 1);
                return Optional.of(ModuleDescriptor.Version.parse(tail));
            } catch (IllegalArgumentException ignore) {}
        }
        return Optional.empty();
    }

    // Parses a set of given service files, and returns a map of service name to list of provider
    // classes.
    static Map<String, List<String>> services(Set<String> serviceFiles, Path path) throws IOException {

View on GitHub (pinned to db6a809a66)

Solutions

  1. Ensure embedded provider artifacts are real .jar files named with the .jar extension.
  2. Check the jar listing resource (the file under IMPL-JARS/<provider>/) — each entry must end in .jar.
  3. If the artifact is an exploded directory, do not route its filename through version(); only jar filenames are valid.
  4. Rebuild/repackage the artifact as a jar.
Defensive patterns

Strategy: validation

Validate before calling

// Require .jar extension before deriving a version from a filename
static Optional<Version> safeVersion(String jarName) {
    if (jarName == null || !jarName.endsWith(".jar")) {
        throw new IllegalArgumentException("unexpected jar name: " + jarName);
    }
    return EmbeddedModulePath.version(jarName);
}

Type guard

static boolean looksLikeJarName(String name) {
    return name != null && name.endsWith(".jar");
}

Try / catch

try {
    return EmbeddedModulePath.version(fileName);
} catch (IllegalArgumentException e) {
    // the artifact is not a jar; repackage or skip version derivation
    return Optional.empty();
}

Prevention

When it happens

Trigger: Calling EmbeddedModulePath.version(name) with a name like 'xlib-2.10.4', 'classes', 'lib.zip', or any string not ending in '.jar'. Indirectly triggered during descriptorForAutomatic when an embedded provider archive is named without the .jar extension.

Common situations: A build/copied artifact that lost its .jar extension (e.g. renamed during shading, or an exploded directory name passed where a jar name was expected). A test passing a path's filename that is a directory. A packaging step that bundles classes without producing a jar.

Related errors


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