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
- Use the full, unedited UniqueId string produced by UniqueId.toString() (e.g. from test reports) — do not hand-type or truncate it.
- If you must construct one programmatically, use UniqueId.root(...).append(...) rather than string concatenation.
- Round-trip-check: format.parse(id.toString()) should reproduce the id; if not, the source string is malformed.
- 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
- Always source UniqueId strings from UniqueId.toString() output, never hand-typed.
- Build UniqueIds with UniqueId.root(...).append(...) rather than string concatenation.
- Sanitize report/log tooling so brackets and colons survive copy-paste.
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
- Could not find any resource(s) with name
- Failed to create a java.net.URI from
- Failed to retrieve canonical path for directory
- Failed to retrieve canonical path for file
- OutputDirectoryCreator not available; probably due to…
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)