{"record":{"id":"a007d37a89afdac0","repo":"elastic/elasticsearch","slug":"invalid-jvm-argument-configure-as-systemprope","errorCode":null,"errorMessage":"Invalid jvm argument `{}` configure as systemProperty instead for {}","messagePattern":"Invalid jvm argument `(.+?)` configure as systemProperty instead for (.+?)","errorType":"validation","errorClass":"TestClustersException","httpStatus":null,"severity":"error","filePath":"build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java","lineNumber":857,"sourceCode":"                .collect(Collectors.joining(\" \"));\n        }\n        if (systemProperties.containsKey(\"io.netty.leakDetection.level\") == false) {\n            systemPropertiesString = systemPropertiesString + \" -Dio.netty.leakDetection.level=paranoid\";\n        }\n\n        String featureFlagsString = \"\";\n        if (featureFlags.isEmpty() == false && isReleasedVersion.apply(getVersion())) {\n            featureFlagsString = featureFlags.stream()\n                .filter(f -> getVersion().onOrAfter(f.getFrom()) && (f.getUntil() == null || getVersion().before(f.getUntil())))\n                .map(f -> \"-D\" + f.getFeature() + \"=true\")\n                .collect(Collectors.joining(\" \"));\n        }\n\n        String jvmArgsString = \"\";\n        if (jvmArgs.isEmpty() == false) {\n            jvmArgsString = \" \" + jvmArgs.stream().peek(argument -> {\n                if (argument.toString().startsWith(\"-D\")) {\n                    throw new TestClustersException(\n                        \"Invalid jvm argument `\" + argument + \"` configure as systemProperty instead for \" + this\n                    );\n                }\n            }).collect(Collectors.joining(\" \"));\n        }\n        String heapSize = System.getProperty(\"tests.heap.size\", \"512m\");\n        defaultEnv.put(\n            \"ES_JAVA_OPTS\",\n            \"-Xms\"\n                + heapSize\n                + \" -Xmx\"\n                + heapSize\n                + \" -ea -esa \"\n                + systemPropertiesString\n                + \" \"\n                + featureFlagsString\n                + \" \"\n                + jvmArgsString","sourceCodeStart":839,"sourceCodeEnd":875,"githubUrl":"https://github.com/elastic/elasticsearch/blob/db6a809a667c081ca1dc7500389d26975573215f/build-tools/src/main/java/org/elasticsearch/gradle/testclusters/ElasticsearchNode.java#L839-L875","documentation":"Thrown by getESEnvironment() while building the jvmArgs portion of ES_JAVA_OPTS when a user-supplied JVM argument starts with '-D'. The build reserves -D system properties for the dedicated systemProperty API (which applies the feature_flag guard, ES_PATH_CONF substitution, and netty leak detection append); mixing them into jvmArgs bypasses those safeguards and is rejected.","triggerScenarios":"Calling node.jvmArgs('-Des.foo=bar') or any argument whose string form starts with '-D'. The check is a prefix match, so even '-Dfoo' (no '=') is rejected. Fires lazily when getESEnvironment() runs.","commonSituations":"Copy-pasting JAVA_OPTS from production into the test cluster. Migrating from a generic Gradle exec to the testcluster API. Assuming jvmArgs and systemProperty are interchangeable.","solutions":["Move any -D... argument to node.systemProperty(key, value) (stripping the -D prefix).","Keep in jvmArgs only non-property flags: -Xmx, -Xms, -XX:..., -ea, etc.","If you need ES_PATH_CONF interpolation, note that systemProperty supports the ${ES_PATH_CONF} placeholder; jvmArgs does not.","Audit existing jvmArgs calls and split them by prefix."],"exampleFix":"// before: -D passed as jvm arg\njvmArgs('-Des.foo=bar', '-Xmx2g')\n// after: split into systemProperty + jvm arg\nsystemProperty('es.foo', 'bar')\njvmArgs('-Xmx2g')","handlingStrategy":"validation","validationCode":"static List<CharSequence> splitJvmAndSystemProperties(List<CharSequence> raw) {\n    List<CharSequence> jvm = new ArrayList<>();\n    for (CharSequence a : raw) {\n        String s = a.toString();\n        if (s.startsWith(\"-D\") && s.contains(\"=\")) {\n            // route to systemProperty instead; caller should handle\n            throw new IllegalArgumentException(\"Use systemProperty for -D arg: \" + s);\n        }\n        jvm.add(a);\n    }\n    return jvm;\n}\n// Use: node.jvmArgs(splitJvmAndSystemProperties(args).toArray(new CharSequence[0]));","typeGuard":"static boolean isPureJvmArg(CharSequence a) {\n    return a != null && !a.toString().startsWith(\"-D\");\n}","tryCatchPattern":null,"preventionTips":["Pass -D... via node.systemProperty(key, value).","Keep jvmArgs to -X, -XX, and non-property flags.","Split existing jvmArgs lists by the '-D' prefix when migrating."],"tags":["testclusters","jvm","validation","config"],"analyzedSha":"db6a809a667c081ca1dc7500389d26975573215f","analyzedAt":"2026-08-12T01:39:14.192Z","schemaVersion":2},"datasetVersion":"2026-08-12T06:17:24.410Z"}