elastic/elasticsearch · error · IllegalStateException
Expected a single original jar, but found: ${oldJarNames}
Error message
Expected a single original jar, but found: ${oldJarNames} What it means
Thrown as IllegalStateException at the start of JarApiComparisonTask.compare() when the oldJar FileCollection resolves to more than one file. The API comparison logic compares exactly one old (reference) jar against one new jar; multiple old jars make the comparison ambiguous and indicate a configuration error in how the old jar dependency was declared.
Source
Thrown at build-tools-internal/src/main/java/org/elasticsearch/gradle/internal/JarApiComparisonTask.java:72
* a non-exported package considered part of the stable api?</li>
* <li>Changing method types to their superclass or return types to an implementation
* class will be considered a change by this approach, even though that doesn't break
* an API.</li>
* <li>Finally, moving a method up the class hierarchy is not really a breaking change,
* but it will trip this test.</li>
* </ol>
*/
@CacheableTask
public abstract class JarApiComparisonTask extends PrecommitTask {
@TaskAction
public void compare() {
FileCollection fileCollection = getOldJar().get();
File newJarFile = getNewJar().get().getSingleFile();
Set<String> oldJarNames = fileCollection.getFiles().stream().map(File::getName).collect(Collectors.toSet());
if (oldJarNames.size() > 1) {
throw new IllegalStateException("Expected a single original jar, but found: " + oldJarNames);
}
if (oldJarNames.contains(newJarFile.getName())) {
throw new IllegalStateException(
"We should be comparing different jars, but original and new jars were both: " + newJarFile.getAbsolutePath()
);
}
JarScanner oldJS = new JarScanner(getOldJar().get().getSingleFile().getPath());
JarScanner newJS = new JarScanner(newJarFile.getPath());
try {
JarScanner.compareSignatures(oldJS.jarSignature(), newJS.jarSignature());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@CompileClasspath
public abstract Property<FileCollection> getOldJar();View on GitHub (pinned to db6a809a66)
Solutions
- Ensure getOldJar() resolves to a configuration containing exactly one jar — use an artifact-specific configuration, not a transitive classpath.
- If using a DRA download, ensure the task type's getOutputs() contains only the jar (the code comment explains DownloadMavenJarTask was created for this reason — line 616).
- Inspect the set in the error message to identify the extra files; trace which dependency/configuration added them.
- Use builtBy and artifact type attributes to narrow the configuration to a single file.
Example fix
// before: oldJar resolves a transitive configuration
getOldJar().from(configurations.named("bwcOldApi")) // pulls transitives → multiple files
// after: create a single-artifact configuration
configurations.register("bwcOldApiJar") {
it.canBeConsumed = false
it.canBeResolved = true
it.dependencies.add(project.dependencies.create("org.elasticsearch:elasticsearch-server:${oldVersion}@jar"))
}
getOldJar().from(configurations.named("bwcOldApiJar")) Defensive patterns
Strategy: validation
Validate before calling
Set<File> files = getOldJar().get().getFiles();
if (files.size() != 1) {
throw new IllegalStateException("oldJar must resolve to exactly one file, found " + files.size() + ": " + files);
} Prevention
- Wire getOldJar() to a single-artifact configuration, not a transitive classpath.
- Use artifact type attributes (@jar) to ensure a single file resolves.
- Use a dedicated task type (like DownloadMavenJarTask) whose outputs contain only the jar.
- Test the configuration resolves to one file before wiring it to the comparison task.
When it happens
Trigger: At line 70, fileCollection.getFiles() is mapped to a Set<String> of filenames. If size > 1 (line 71), the exception fires. This happens when getOldJar() resolves a configuration that contains multiple jars — e.g., a compile classpath instead of a single artifact, or a configuration that pulls transitive jars.
Common situations: The oldJar property was wired to a resolvable configuration that includes transitive dependencies; the DRA/BWC dependency resolution returned both the jar and a parent directory artifact; a Copy task's output directory leaked into the file collection (noted in the code comment at line 611-613); misconfiguration where oldJar is a classpath rather than a single artifact.
Related errors
- We should be comparing different jars, but original and new
- tests.bwc.git_fetch_latest must be [true] or [false] but was
- Invalid tests.bwc.mode value [${mode}]. Must be one of: grad
- Configuring a snapshot bwc distribution ('${distribution.get
- Elasticsearch version is missing from properties.
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/e0446962df1524b2.
Report an issue: GitHub.