elastic/elasticsearch · error · IllegalStateException
testcluster does not allow overwriting the following env var
Error message
testcluster does not allow overwriting the following env vars {} for {} What it means
Thrown by getESEnvironment() (as IllegalStateException, not TestClustersException) when the user-set environment map shares keys with the build's reserved default environment. The node owns ES_JAVA_HOME, ES_PATH_CONF, ES_JAVA_OPTS, ES_TMPDIR, TMP, HOSTNAME, COMPUTERNAME, and (when set) PATH; overwriting them would break startup reproducibility, so the intersection is rejected wholesale.
Source
Thrown at build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java:899
defaultEnv.put("ES_TMPDIR", tmpDir.toString());
// Windows requires this as it defaults to `c:\windows` despite ES_TMPDIR
defaultEnv.put("TMP", tmpDir.toString());
// Override the system hostname variables for testing
defaultEnv.put("HOSTNAME", HOSTNAME_OVERRIDE);
defaultEnv.put("COMPUTERNAME", COMPUTERNAME_OVERRIDE);
// Propagate PATH so shell scripts (e.g. elasticsearch-keystore) can locate standard utilities
// such as `dirname`. This is essential on systems like NixOS where PATH is non-standard.
String systemPath = System.getenv("PATH");
if (systemPath != null) {
defaultEnv.put("PATH", systemPath);
}
Set<String> commonKeys = new HashSet<>(environment.keySet());
commonKeys.retainAll(defaultEnv.keySet());
if (commonKeys.isEmpty() == false) {
throw new IllegalStateException("testcluster does not allow overwriting the following env vars " + commonKeys + " for " + this);
}
environment.forEach((key, value) -> defaultEnv.put(key, value.toString()));
return defaultEnv;
}
private void startElasticsearchProcess() {
final ProcessBuilder processBuilder = new ProcessBuilder();
Path effectiveDistroDir = getDistroDir();
List<String> command = OS.<List<String>>conditional()
.onUnix(() -> List.of(effectiveDistroDir.resolve("./bin/elasticsearch").toString()))
.onWindows(() -> Arrays.asList("cmd", "/c", effectiveDistroDir.resolve("bin\\elasticsearch.bat").toString()))
.supply();
processBuilder.command(command);
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();View on GitHub (pinned to db6a809a66)
Solutions
- Remove the conflicting key(s) printed in the message from your environment(...) calls.
- For JVM tuning, use -Dtests.heap.size or node.jvmArgs rather than ES_JAVA_OPTS.
- For ES_PATH_CONF / ES_TMPDIR / TMP, rely on the build's own management; do not set them.
- If you need a custom PATH on a system where System.getenv('PATH') is null, set it via the OS or a Gradle init script rather than node.environment.
Example fix
// before: conflicts with reserved ES_JAVA_OPTS
environment('ES_JAVA_OPTS', '-Xmx4g')
// after: tune heap via the supported knob
systemProperty('tests.heap.size', '4g') // or pass -Dtests.heap.size=4g on the Gradle CLI Defensive patterns
Strategy: validation
Validate before calling
static final Set<String> RESERVED_ENV = Set.of(
"ES_JAVA_HOME", "ES_PATH_CONF", "ES_JAVA_OPTS", "ES_TMPDIR", "TMP", "HOSTNAME", "COMPUTERNAME", "PATH"
);
static void assertNoReservedEnv(Map<String,String> env) {
Set<String> clash = new HashSet<>(env.keySet());
clash.retainAll(RESERVED_ENV);
if (!clash.isEmpty()) {
throw new IllegalArgumentException("Refusing reserved env keys " + clash
+ "; use tests.heap.size / jvmArgs / OS config instead.");
}
}
// Use: assertNoReservedEnv(myEnv); node.environment(myEnv); Type guard
static boolean isReservedEnvKey(String k) {
return RESERVED_ENV.contains(k);
} Prevention
- Never set ES_JAVA_OPTS/ES_PATH_CONF/ES_TMPDIR/TMP via node.environment.
- Tune heap with -Dtests.heap.size or node.jvmArgs.
- Let the build manage HOSTNAME/COMPUTERNAME/PATH.
When it happens
Trigger: Calling node.environment('ES_JAVA_OPTS', '...') or any of the reserved keys. The check computes the intersection of user keys with defaultEnv keys (after defaults are populated) and throws if non-empty. Fires lazily at start()/exec time.
Common situations: Trying to tune JVM heap via ES_JAVA_OPTS (use tests.heap.size or jvmArgs instead). Setting TMP/ES_TMPDIR explicitly. Forwarding HOSTNAME/COMPUTERNAME from CI. Setting PATH on a system that already propagates one (the code copies the system PATH into defaults when non-null).
Related errors
- Can't create extra config file from {} for {} as it does not
- extra jar file {} doesn't appear to be a JAR
- Not a valid module {} for {}
- extra config file destination can't be relative, was {} for
- Unknown keys in user definition {} for {}
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/286d6dae63523f32.
Report an issue: GitHub.