elastic/elasticsearch · error · TestClustersException

Can't create extra config file from {} for {} as it does not

Error message

Can't create extra config file from {} for {} as it does not exist

What it means

Thrown by copyExtraConfigFiles() during start() when an entry registered via extraConfigFile(destination, from) points to a source File whose path does not exist (Files.exists check). The node refuses to copy a non-existent file into its config directory because that would silently produce a missing config entry and a confusing downstream startup failure.

Source

Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java:618

        if (currentDistro + 1 >= distributions.size()) {
            throw new TestClustersException("Ran out of versions to go to for " + this);
        }
        logToProcessStdout("Switch version from " + getVersion() + " to " + distributions.get(currentDistro + 1).getVersion());
        currentDistro += 1;
        setting("node.attr.upgraded", "true");
    }

    private boolean isSettingTrue(String name) {
        return Boolean.parseBoolean(settings.getOrDefault(name, "false").toString());
    }

    private void copyExtraConfigFiles() {
        if (extraConfigFiles.isEmpty() == false) {
            logToProcessStdout("Setting up " + extraConfigFiles.size() + " additional config files");
        }
        extraConfigFiles.forEach((destination, from) -> {
            if (Files.exists(from.toPath()) == false) {
                throw new TestClustersException("Can't create extra config file from " + from + " for " + this + " as it does not exist");
            }
            Path dst = configFile.getParent().resolve(destination);
            try {
                Files.createDirectories(dst.getParent());
                Files.copy(from.toPath(), dst, StandardCopyOption.REPLACE_EXISTING);
                LOGGER.info("Added extra config file {} for {}", destination, this);
            } catch (IOException e) {
                throw new UncheckedIOException("Can't create extra config file for", e);
            }
        });
    }

    /**
     * Copies extra jars to the `/lib` directory.
     * //TODO: Remove this when system modules are available
     */
    private void copyExtraJars() {
        List<File> extraJarFiles = this.extraJarConfigurations.stream()

View on GitHub (pinned to db6a809a66)

Solutions

  1. Verify the path from the message exists at the moment the cluster starts.
  2. If generated, pass a Provider<File> or wire the producing task as a dependency so Gradle materialises it first.
  3. Use project.layout.projectDirectory.file('config/x.xml') for checked-in files to make the path unambiguous.
  4. Remove the extraConfigFile call if the file is no longer needed; do not register dead config.

Example fix

// before: file does not exist yet
extraConfigFile('log4j2.xml', new File('src/test/resources/log4j2.xml'))
// after: resolve from project dir and ensure the file is committed
extraConfigFile('log4j2.xml', project.layout.projectDirectory.file('src/test/resources/log4j2.xml').asFile)
Defensive patterns

Strategy: validation

Validate before calling

node.getExtraConfigFiles().forEach((dest, from) -> {
    if (!Files.exists(from.toPath())) {
        throw new IllegalStateException("extraConfigFile source missing for dest " + dest + ": " + from);
    }
});
// Prefer Provider<File> sourced from the producing task so Gradle materialises it before start().

Prevention

When it happens

Trigger: Calling extraConfigFile('log4j2.xml', file) where file resolves to a path that does not exist at start time. Because the map stores File references lazily, the check fires during start() rather than at registration, so a typo or a not-yet-generated source surfaces here.

Common situations: Source config file generated by another Gradle task that was not wired as a dependency. Path relative to the wrong subproject. Typo in filename. Resource stripped on a branch. CI checkout missing the file.

Related errors


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