oracle/graal · error · UnsupportedRegexException
Independent non-capturing groups are not supported
Error message
Independent non-capturing groups are not supported
What it means
Thrown by the Java-flavor lexer for the atomic/independent group construct (?>...), which prevents backtracking into the group once it has matched. TRegex's compilation pipeline has no mechanism to seal a subexpression against backtracking (its executors either match purely or backtrack exhaustively), so parseCustomGroupBeginQ rejects the '>' after '(?' with UnsupportedRegexException.
Source
Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexLexer.java:910
@Override
protected int parseCustomEscapeCharFallback(int c, boolean inCharClass) {
// any non-alphabetic character can be used after an escape
// digits are not accepted here since they should have been parsed as octal sequence or
// backreference earlier
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {
throw syntaxError(JavaErrorMessages.ILLEGAL_ESCAPE_SEQUENCE, ErrorCode.InvalidEscape);
}
return c;
}
@Override
protected Token parseCustomGroupBeginQ(char charAfterQuestionMark) {
// TODO do we need to do something else than inline flags? check "Special constructs" in
// documentation
char c = charAfterQuestionMark;
if (c == '>') {
throw new UnsupportedRegexException("Independent non-capturing groups are not supported");
}
int firstCharPos = position;
JavaFlags newFlags = getLocalFlags();
while (JavaFlags.isValidFlagChar(c)) {
newFlags = newFlags.addFlag(c);
if (atEnd()) {
throw handleUnfinishedGroupQ();
}
c = consumeChar();
}
if (c == '-') {
if (atEnd()) {
throw handleUnfinishedGroupQ();
}
c = consumeChar();
while (JavaFlags.isValidFlagChar(c)) {
newFlags = newFlags.delFlag(c);
if (atEnd()) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Replace (?>X) with a plain non-capturing group (?:X) and accept the backtracking semantics difference
- If atomicity guarded against catastrophic backtracking, restructure the pattern (e.g. unroll loops, use negated character classes) to bound the search instead
- Verify correctness after the rewrite: (?:X)+ and (?>X)+ can produce different matches on partial-match inputs
Example fix
// before String p = "(?>\\d+)\\."; // after String p = "(?:\\d+)\\.";
Defensive patterns
Strategy: validation
Validate before calling
boolean usesAtomicGroup(String pattern) {
return pattern.contains("(?>");
} Try / catch
try {
RegexObject re = compileJavaFlavor(pattern);
} catch (UnsupportedRegexException e) {
pattern = pattern.replace("(?>", "(?:"); // semantics differ: verify matches still correct
re = compileJavaFlavor(pattern);
} Prevention
- Lint incoming patterns for '(?>' before compiling on TRegex
- Prefer structural ReDoS defenses (anchors, negated classes) over atomic groups for portable patterns
When it happens
Trigger: Compiling any Java-flavor pattern containing (?> ... ), e.g. "(?>ab)+c" or possessive-style optimizations like "(?>\\d+)\\.". The lexer sees '(' then '?' then '>' in parseCustomGroupBeginQ and throws before parsing group contents.
Common situations: ReDoS-hardening guides recommend atomic groups; developers copy PCRE/Perl/.NET patterns using (?>...) for performance; patterns migrated from Rust/RE2-style possessive notation into a TRegex-backed engine.
Related errors
- Extended grapheme cluster boundaries are not supported
- Grapheme clusters are not supported
- End of previous match boundary matcher is not supported
- Literal escape not supported in this context
- possessive quantifiers are not supported
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/55ce6a0c8fc673b3.
Report an issue: GitHub.