{"record":{"id":"1b90f3e909eecc28","repo":"junit-team/junit5","slug":"duplicate-key-s-for-values-s-and-s","errorCode":null,"errorMessage":"Duplicate key '%s' for values '%s' and '%s'.","messagePattern":"Duplicate key '(.+?)' for values '(.+?)' and '(.+?)'\\.","errorType":"validation","errorClass":"CommandLine.ParameterException","httpStatus":null,"severity":"error","filePath":"junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java","lineNumber":242,"sourceCode":"\t\t * on the command line.\n\t\t *\n\t\t * @param map the key-value pairs to add\n\t\t * @throws CommandLine.ParameterException if the map already contains this key\n\t\t * @see <a href=\"https://github.com/junit-team/junit-framework/issues/1308\">#1308</a>\n\t\t */\n\t\t@Option(names = \"--config\", paramLabel = \"KEY=VALUE\", arity = \"1\", description = \"Set a configuration parameter for test discovery and execution. This option can be repeated.\")\n\t\tpublic void setConfigurationParameters(Map<String, String> map) {\n\t\t\tfor (String key : map.keySet()) {\n\t\t\t\tString newValue = map.get(key);\n\t\t\t\tvalidateUnique(key, newValue);\n\t\t\t\tconfigurationParameters.put(key, newValue);\n\t\t\t}\n\t\t}\n\n\t\tprivate void validateUnique(String key, String newValue) {\n\t\t\tString existing = configurationParameters.get(key);\n\t\t\tif (existing != null && !existing.equals(newValue)) {\n\t\t\t\tthrow new CommandLine.ParameterException(spec.commandLine(),\n\t\t\t\t\t\"Duplicate key '%s' for values '%s' and '%s'.\".formatted(key, existing, newValue));\n\t\t\t}\n\t\t}\n\n\t\tprivate void applyTo(TestDiscoveryOptions result) {\n\t\t\tresult.setAdditionalClasspathEntries(additionalClasspathEntries);\n\t\t\tresult.setConfigurationParametersResources(configurationParametersResources);\n\t\t\tresult.setConfigurationParameters(configurationParameters);\n\t\t}\n\t}\n\n\tpublic TestDiscoveryOptions toTestDiscoveryOptions() {\n\t\tTestDiscoveryOptions result = new TestDiscoveryOptions();\n\t\tif (this.selectorOptions != null) {\n\t\t\tthis.selectorOptions.applyTo(result);\n\t\t}\n\t\tif (this.filterOptions != null) {\n\t\t\tthis.filterOptions.applyTo(result);","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/junit-team/junit5/blob/f070c699a08b5d8393df9afd147af5c5e90bb21b/junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java#L224-L260","documentation":"TestDiscoveryOptionsMixin.validateUnique() enforces that a --config KEY=VALUE pair, when repeated with the same key, must supply the same value. If the same key is given with a conflicting value, picocli.ParameterException is thrown, so the CLI prints usage and exits with an error code. Same-key/same-value is allowed (idempotent).","triggerScenarios":"Using --config multiple times with the same KEY but different VALUE in one invocation: e.g. '--config junit.jupiter.displayname.generator.default=A --config junit.jupiter.displayname.generator.default=B'. picocli accumulates them into the map; the setter detects the conflict.","commonSituations":"Build scripts or Gradle/Maven plugins that concatenate --config from multiple sources and accidentally disagree on a value; copy-pasted CLI args where one value is stale; CI templates overriding a config that was set earlier in the same command line.","solutions":["Search the full command line for repeated --config entries with the same KEY and reconcile to a single VALUE.","Centralize configuration parameter values in one place (a script variable) so they cannot diverge.","If both values are legitimately needed, use two different keys or pick one as authoritative.","Re-run after removing the conflicting duplicate."],"exampleFix":"# before\njava -jar junit-platform-console-standalone.jar execute --config junit.jupiter.testinstance.lifecycle.default=per_class --config junit.jupiter.testinstance.lifecycle.default=per_method\n# after: pick one value\njava -jar junit-platform-console-standalone.jar execute --config junit.jupiter.testinstance.lifecycle.default=per_method","handlingStrategy":"validation","validationCode":"// In a wrapper, dedupe --config KEY=VALUE pairs and detect conflicts before invoking the launcher.\nimport java.util.*;\nMap<String,String> seen = new HashMap<>();\nList<String> configArgs = collectConfigArgs(args); // e.g. each \"--config KEY=VALUE\"\nfor (String a : configArgs) {\n  String[] kv = a.replaceFirst(\"^--config\\\\s+=?\", \"\").split(\"=\", 2);\n  String key = kv[0].trim(), val = kv.length > 1 ? kv[1] : \"\";\n  if (seen.containsKey(key) && !seen.get(key).equals(val))\n    throw new IllegalArgumentException(\"Conflicting --config for key '\" + key + \"': '\" + seen.get(key) + \"' vs '\" + val + \"'\");\n  seen.put(key, val);\n}\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Source all --config values from a single map in your build script so they cannot diverge.","Treat any repeated --config key as a red flag during code/script review.","Document the canonical config values once and reuse them everywhere."],"tags":["cli","picocli","configuration","argument-validation","console-launcher"],"backgroundTag":null,"analyzedSha":"f070c699a08b5d8393df9afd147af5c5e90bb21b","analyzedAt":"2026-08-11T20:31:00.530Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}