junit-team/junit5 · error · JUnitException

' ' is not a well-formed UniqueId segment

Error message

'%s' is not a well-formed UniqueId segment

What it means

UniqueIdFormat.parse() splits a UniqueId string by the segment delimiter and runs each segment through createSegment(), which matches against the expected '[type:value]' pattern. If a segment does not match the format, a JUnitException is thrown. This is how the engine reports a malformed UniqueId during parsing.

Solutions

  1. Use the full, unedited UniqueId string produced by UniqueId.toString() (e.g. from test reports) — do not hand-type or truncate it.
  2. If you must construct one programmatically, use UniqueId.root(...).append(...) rather than string concatenation.
  3. Round-trip-check: format.parse(id.toString()) should reproduce the id; if not, the source string is malformed.
  4. Sanitize logs/tools that may strip the '[', ']', or ':' characters.

Example fix

// before
UniqueId id = UniqueId.forString("[engine:junit-jupiter]/MyTestClass"); // second segment missing [type:value]
// after
UniqueId id = UniqueId.forString("[engine:junit-jupiter]/[class:com.example.MyTestClass]");
Defensive patterns

Strategy: validation

Validate before calling

// Validate a UniqueId string parses before using it.
import org.junit.platform.engine.UniqueId;
String src = getIdFromExternalSource();
try {
  UniqueId parsed = UniqueId.forString(src);
} catch (org.junit.platform.commons.JUnitException e) {
  throw new IllegalArgumentException("Not a valid UniqueId string: " + src, e);
}

Type guard

// A cheap structural guard before full parse: each segment must be [type:value].
boolean looksLikeUniqueId(String s) {
  if (s == null || s.isEmpty()) return false;
  for (String seg : s.split("\\]/\\[|-/")) { // rough split; rely on parse() for full validation
    if (!seg.matches(".*:.*")) return false;
  }
  return s.contains("[") && s.contains("]") && s.contains(":");
}

Try / catch

try { UniqueId id = UniqueId.forString(raw); } catch (org.junit.platform.commons.JUnitException e) { /* log raw, fall back to a broader selector */ }

Prevention

When it happens

Trigger: Calling UniqueId.forString / format.parse with a string whose segments are missing the '[type:value]' structure — unbalanced brackets, a missing type:value separator, or stray delimiters. Common when a UniqueId is hand-constructed, truncated, or corrupted (e.g. logged then copy-pasted with truncation).

Common situations: Passing a test id from a report/XML that was edited or truncated; building a selector from a fragment that is not a full UniqueId; copy-paste losing bracket characters; a tool that strips square brackets from logs.

Related errors


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

Appendix: source

Thrown at junit-platform-engine/src/main/java/org/junit/platform/engine/UniqueIdFormat.java:97

		encodedCharacterMap.computeIfAbsent(segmentDelimiter, UniqueIdFormat::encode);
	}

	/**
	 * Parse a {@code UniqueId} from the supplied string representation.
	 *
	 * @return a properly constructed {@code UniqueId}
	 * @throws JUnitException if the string cannot be parsed
	 */
	UniqueId parse(String source) throws JUnitException {
		String[] parts = source.split(String.valueOf(this.segmentDelimiter));
		List<Segment> segments = Arrays.stream(parts).map(this::createSegment).toList();
		return new UniqueId(segments);
	}

	private Segment createSegment(String segmentString) throws JUnitException {
		Matcher segmentMatcher = this.segmentPattern.matcher(segmentString);
		if (!segmentMatcher.matches()) {
			throw new JUnitException("'%s' is not a well-formed UniqueId segment".formatted(segmentString));
		}
		String type = decode(checkAllowed(segmentMatcher.group(1)));
		String value = decode(checkAllowed(segmentMatcher.group(2)));
		return new Segment(type, value);
	}

	private String checkAllowed(String typeOrValue) {
		checkDoesNotContain(typeOrValue, this.segmentDelimiter);
		checkDoesNotContain(typeOrValue, this.typeValueSeparator);
		checkDoesNotContain(typeOrValue, this.openSegment);
		checkDoesNotContain(typeOrValue, this.closeSegment);
		return typeOrValue;
	}

	private void checkDoesNotContain(String typeOrValue, char forbiddenCharacter) {
		Preconditions.condition(typeOrValue.indexOf(forbiddenCharacter) < 0,
			() -> "type or value '%s' must not contain '%s'".formatted(typeOrValue, forbiddenCharacter));
	}

View on GitHub (pinned to f070c699a0)