elastic/elasticsearch · error · TestClustersException

Failed to set the keystore password for {}

Error message

Failed to set the keystore password for {}

What it means

Thrown by startElasticsearchProcess() when Files.writeString(esInputFile, keystorePassword+'\n', CREATE) raises an IOException, wrapped as TestClustersException with the node identity. The code writes the keystore password to a temp file so it can be fed to the ES process via processBuilder.redirectInput; failing to materialise that file means the secured keystore cannot be unlocked at boot.

Source

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

        processBuilder.directory(workingDir.toFile());
        Map<String, String> environment = processBuilder.environment();
        // Don't inherit anything from the environment for as that would lack reproducibility
        environment.clear();
        environment.putAll(getESEnvironment());
        String cliJvmArgsString = String.join(" ", cliJvmArgs);
        environment.put("CLI_JAVA_OPTS", cliJvmArgsString + " " + System.getProperty("tests.jvm.argline", ""));

        // Direct the stderr to the ES log file. This should capture any jvm problems to start.
        // Stdout is discarded because ES duplicates the log file to stdout when run in the foreground.
        processBuilder.redirectOutput(ProcessBuilder.Redirect.appendTo(esOutputFile.toFile()));
        processBuilder.redirectErrorStream(true);

        if (keystorePassword != null && keystorePassword.length() > 0) {
            try {
                Files.writeString(esInputFile, keystorePassword + "\n", StandardOpenOption.CREATE);
                processBuilder.redirectInput(esInputFile.toFile());
            } catch (IOException e) {
                throw new TestClustersException("Failed to set the keystore password for " + this, e);
            }
        }
        LOGGER.info("Running `{}` in `{}` for {} env: {}", command, workingDir, this, environment);
        Process esProcess;
        try {
            esProcess = processBuilder.start();
        } catch (IOException e) {
            throw new TestClustersException("Failed to start ES process for " + this, e);
        }
        testClustersRegistryProvider.get().storeProcess(id(), esProcess);
        reaperServiceProvider.get().registerPid(toString(), esProcess.pid());
    }

    @Internal
    public Path getDistroDir() {
        return canUseSharedDistribution()
            ? getExtractedDistributionDir().toFile().listFiles()[0].toPath()
            : workingDir.resolve("distro").resolve(getVersion() + "-" + testDistribution);

View on GitHub (pinned to db6a809a66)

Solutions

  1. Read the cause IOException for the precise FS error (message vs permission vs space).
  2. Clean build/testclusters/<node> so esInputFile and its parent are recreated.
  3. Free disk / inodes and fix ownership on the build dir.
  4. Shorten the build path on Windows to avoid MAX_PATH for the input file.
Defensive patterns

Strategy: try-catch

Validate before calling

// Confirm the esInputFile parent is writable before start
Path inputParent = node.getEsInputFile().getParent();
if (Files.exists(inputParent) && !Files.isWritable(inputParent)) {
    throw new IllegalStateException("esInputFile dir not writable: " + inputParent);
}
if (node.getKeystorePassword() != null && node.getKeystorePassword().length() > 0
        && OS.current() == OS.WINDOWS && node.getEsInputFile().toString().length() > 240) {
    throw new IllegalStateException("esInputFile path too long for Windows: " + node.getEsInputFile());
}

Try / catch

try {
    node.start();
} catch (TestClustersException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Failed to set the keystore password")) {
        throw new IllegalStateException("Keystore password file write failed (disk/perms/path): "
            + e.getCause(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A non-empty keystorePassword is configured, and writing it to esInputFile fails: parent dir missing/ read-only, disk full, permission residue, path-too-long, or antivirus interference. The check fires at process launch time inside start().

Common situations: Disk full on CI agent. Permission residue from a root-owned prior run. Windows MAX_PATH in the esInputFile path. Antivirus locking temp files. Stale build/testclusters after a host change.

Related errors


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