{"record":{"id":"a10e5645c5edbd4b","repo":"elastic/elasticsearch","slug":"unexpected-jar-name-jarname","errorCode":null,"errorMessage":"unexpected jar name: {jarName}","messagePattern":"unexpected jar name: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"libs/core/src/main/java/org/elasticsearch/core/internal/provider/EmbeddedModulePath.java","lineNumber":160,"sourceCode":"\n    // Scans a given path for class files and services.\n    static ScanResult scan(Path path) throws IOException {\n        try (var stream = Files.walk(path)) {\n            Map<Boolean, Set<String>> map = stream.filter(p -> Files.isDirectory(p) == false)\n                .map(p -> path.relativize(p).toString())\n                .filter(p -> (p.endsWith(\".class\") ^ p.startsWith(SERVICES_PREFIX)))\n                .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX), Collectors.toSet()));\n            return new ScanResult(map.get(Boolean.FALSE), map.get(Boolean.TRUE));\n        }\n    }\n\n    private static final Pattern DASH_VERSION = Pattern.compile(\"-(\\\\d+(\\\\.|$))\");\n\n    // Determines the module version (of an automatic module), given the jar file name. As per,\n    // https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/lang/module/ModuleFinder.html#of(java.nio.file.Path...)\n    static Optional<ModuleDescriptor.Version> version(String jarName) {\n        if (jarName.endsWith(\".jar\") == false) {\n            throw new IllegalArgumentException(\"unexpected jar name: \" + jarName);\n        }\n        // drop \".jar\"\n        String name = jarName.substring(0, jarName.length() - 4);\n        // find first occurrence of -${NUMBER}. or -${NUMBER}$\n        Matcher matcher = DASH_VERSION.matcher(name);\n        if (matcher.find()) {\n            int start = matcher.start();\n            try {\n                String tail = name.substring(start + 1);\n                return Optional.of(ModuleDescriptor.Version.parse(tail));\n            } catch (IllegalArgumentException ignore) {}\n        }\n        return Optional.empty();\n    }\n\n    // Parses a set of given service files, and returns a map of service name to list of provider\n    // classes.\n    static Map<String, List<String>> services(Set<String> serviceFiles, Path path) throws IOException {","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/libs/core/src/main/java/org/elasticsearch/core/internal/provider/EmbeddedModulePath.java#L142-L178","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Ensure embedded provider artifacts are real .jar files named with the .jar extension.","Check the jar listing resource (the file under IMPL-JARS/<provider>/) — each entry must end in .jar.","If the artifact is an exploded directory, do not route its filename through version(); only jar filenames are valid.","Rebuild/repackage the artifact as a jar."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Require .jar extension before deriving a version from a filename\nstatic Optional<Version> safeVersion(String jarName) {\n    if (jarName == null || !jarName.endsWith(\".jar\")) {\n        throw new IllegalArgumentException(\"unexpected jar name: \" + jarName);\n    }\n    return EmbeddedModulePath.version(jarName);\n}","typeGuard":"static boolean looksLikeJarName(String name) {\n    return name != null && name.endsWith(\".jar\");\n}","tryCatchPattern":"try {\n    return EmbeddedModulePath.version(fileName);\n} catch (IllegalArgumentException e) {\n    // the artifact is not a jar; repackage or skip version derivation\n    return Optional.empty();\n}","preventionTips":["Name embedded provider archives with the .jar extension only.","Keep the IMPL-JARS listing entries ending in .jar.","Do not pass directory names or zip files to version().","Validate filenames before automatic-module descriptor building."],"tags":["modules","jpms","packaging","provider","elasticsearch-core","validation"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}