{"record":{"id":"286d6dae63523f32","repo":"elastic/elasticsearch","slug":"testcluster-does-not-allow-overwriting-the-followi","errorCode":null,"errorMessage":"testcluster does not allow overwriting the following env vars {} for {}","messagePattern":"testcluster does not allow overwriting the following env vars (.+?) for (.+?)","errorType":"validation","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java","lineNumber":899,"sourceCode":"        defaultEnv.put(\"ES_TMPDIR\", tmpDir.toString());\n        // Windows requires this as it defaults to `c:\\windows` despite ES_TMPDIR\n        defaultEnv.put(\"TMP\", tmpDir.toString());\n\n        // Override the system hostname variables for testing\n        defaultEnv.put(\"HOSTNAME\", HOSTNAME_OVERRIDE);\n        defaultEnv.put(\"COMPUTERNAME\", COMPUTERNAME_OVERRIDE);\n\n        // Propagate PATH so shell scripts (e.g. elasticsearch-keystore) can locate standard utilities\n        // such as `dirname`. This is essential on systems like NixOS where PATH is non-standard.\n        String systemPath = System.getenv(\"PATH\");\n        if (systemPath != null) {\n            defaultEnv.put(\"PATH\", systemPath);\n        }\n\n        Set<String> commonKeys = new HashSet<>(environment.keySet());\n        commonKeys.retainAll(defaultEnv.keySet());\n        if (commonKeys.isEmpty() == false) {\n            throw new IllegalStateException(\"testcluster does not allow overwriting the following env vars \" + commonKeys + \" for \" + this);\n        }\n\n        environment.forEach((key, value) -> defaultEnv.put(key, value.toString()));\n        return defaultEnv;\n    }\n\n    private void startElasticsearchProcess() {\n        final ProcessBuilder processBuilder = new ProcessBuilder();\n        Path effectiveDistroDir = getDistroDir();\n        List<String> command = OS.<List<String>>conditional()\n            .onUnix(() -> List.of(effectiveDistroDir.resolve(\"./bin/elasticsearch\").toString()))\n            .onWindows(() -> Arrays.asList(\"cmd\", \"/c\", effectiveDistroDir.resolve(\"bin\\\\elasticsearch.bat\").toString()))\n            .supply();\n        processBuilder.command(command);\n        processBuilder.directory(workingDir.toFile());\n        Map<String, String> environment = processBuilder.environment();\n        // Don't inherit anything from the environment for as that would lack reproducibility\n        environment.clear();","sourceCodeStart":881,"sourceCodeEnd":917,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java#L881-L917","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before: conflicts with reserved ES_JAVA_OPTS\nenvironment('ES_JAVA_OPTS', '-Xmx4g')\n// after: tune heap via the supported knob\nsystemProperty('tests.heap.size', '4g') // or pass -Dtests.heap.size=4g on the Gradle CLI","handlingStrategy":"validation","validationCode":"static final Set<String> RESERVED_ENV = Set.of(\n    \"ES_JAVA_HOME\", \"ES_PATH_CONF\", \"ES_JAVA_OPTS\", \"ES_TMPDIR\", \"TMP\", \"HOSTNAME\", \"COMPUTERNAME\", \"PATH\"\n);\nstatic void assertNoReservedEnv(Map<String,String> env) {\n    Set<String> clash = new HashSet<>(env.keySet());\n    clash.retainAll(RESERVED_ENV);\n    if (!clash.isEmpty()) {\n        throw new IllegalArgumentException(\"Refusing reserved env keys \" + clash\n            + \"; use tests.heap.size / jvmArgs / OS config instead.\");\n    }\n}\n// Use: assertNoReservedEnv(myEnv); node.environment(myEnv);","typeGuard":"static boolean isReservedEnvKey(String k) {\n    return RESERVED_ENV.contains(k);\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["testclusters","environment","validation","config"],"backgroundTag":null,"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}