junit-team/junit5 · error · CommandLine.ParameterException

Duplicate key ' ' for values ' ' and ' '.

Error message

Duplicate key '%s' for values '%s' and '%s'.

What it means

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).

Solutions

  1. Search the full command line for repeated --config entries with the same KEY and reconcile to a single VALUE.
  2. Centralize configuration parameter values in one place (a script variable) so they cannot diverge.
  3. If both values are legitimately needed, use two different keys or pick one as authoritative.
  4. Re-run after removing the conflicting duplicate.

Example fix

# before
java -jar junit-platform-console-standalone.jar execute --config junit.jupiter.testinstance.lifecycle.default=per_class --config junit.jupiter.testinstance.lifecycle.default=per_method
# after: pick one value
java -jar junit-platform-console-standalone.jar execute --config junit.jupiter.testinstance.lifecycle.default=per_method
Defensive patterns

Strategy: validation

Validate before calling

// In a wrapper, dedupe --config KEY=VALUE pairs and detect conflicts before invoking the launcher.
import java.util.*;
Map<String,String> seen = new HashMap<>();
List<String> configArgs = collectConfigArgs(args); // e.g. each "--config KEY=VALUE"
for (String a : configArgs) {
  String[] kv = a.replaceFirst("^--config\\s+=?", "").split("=", 2);
  String key = kv[0].trim(), val = kv.length > 1 ? kv[1] : "";
  if (seen.containsKey(key) && !seen.get(key).equals(val))
    throw new IllegalArgumentException("Conflicting --config for key '" + key + "': '" + seen.get(key) + "' vs '" + val + "'");
  seen.put(key, val);
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of junit-team/junit5@f070c699a0 (2026-08-11). Data as JSON: /api/errors/1b90f3e909eecc28. Report an issue: GitHub.

Appendix: source

Thrown at junit-platform-console/src/main/java/org/junit/platform/console/options/TestDiscoveryOptionsMixin.java:242

		 * on the command line.
		 *
		 * @param map the key-value pairs to add
		 * @throws CommandLine.ParameterException if the map already contains this key
		 * @see <a href="https://github.com/junit-team/junit-framework/issues/1308">#1308</a>
		 */
		@Option(names = "--config", paramLabel = "KEY=VALUE", arity = "1", description = "Set a configuration parameter for test discovery and execution. This option can be repeated.")
		public void setConfigurationParameters(Map<String, String> map) {
			for (String key : map.keySet()) {
				String newValue = map.get(key);
				validateUnique(key, newValue);
				configurationParameters.put(key, newValue);
			}
		}

		private void validateUnique(String key, String newValue) {
			String existing = configurationParameters.get(key);
			if (existing != null && !existing.equals(newValue)) {
				throw new CommandLine.ParameterException(spec.commandLine(),
					"Duplicate key '%s' for values '%s' and '%s'.".formatted(key, existing, newValue));
			}
		}

		private void applyTo(TestDiscoveryOptions result) {
			result.setAdditionalClasspathEntries(additionalClasspathEntries);
			result.setConfigurationParametersResources(configurationParametersResources);
			result.setConfigurationParameters(configurationParameters);
		}
	}

	public TestDiscoveryOptions toTestDiscoveryOptions() {
		TestDiscoveryOptions result = new TestDiscoveryOptions();
		if (this.selectorOptions != null) {
			this.selectorOptions.applyTo(result);
		}
		if (this.filterOptions != null) {
			this.filterOptions.applyTo(result);

View on GitHub (pinned to f070c699a0)