oracle/graal · error · UnsupportedRegexException
possessive quantifiers are not supported
Error message
possessive quantifiers are not supported
What it means
Thrown by the Java-flavor parser when a quantifier is marked possessive (*+, ++, ?+, {n,m}+). Possessive quantifiers swallow the input without ever giving it back, which requires cut semantics in the matcher; TRegex's NFA/DFA executors cannot express that, so JavaRegexParser checks quantifier.isPossessive() on the parsed token and throws UnsupportedRegexException.
Source
Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexParser.java:164
case nonWordBoundary:
if (lexer.getLocalFlags().isUnicodeCharacterClass()) {
buildWordNonBoundaryAssertion(lexer.unicode.word, lexer.unicode.nonWord);
} else {
buildWordNonBoundaryAssertion(Constants.WORD_CHARS, Constants.NON_WORD_CHARS);
}
break;
case backReference:
astBuilder.addBackReference((Token.BackReference) token, getFlags().isCaseInsensitive(), getFlags().isUnicodeCase() || getFlags().isUnicodeCharacterClass());
break;
case quantifier:
Token.Quantifier quantifier = (Token.Quantifier) token;
// quantifiers of type *, + or ? cannot directly follow another quantifier
if (last instanceof Token.Quantifier && quantifier.isSingleChar()) {
throw syntaxErrorHere(JavaErrorMessages.danglingMetaCharacter(quantifier), ErrorCode.InvalidQuantifier);
}
if (astBuilder.getCurTerm() != null) {
if (quantifier.isPossessive()) {
throw new UnsupportedRegexException("possessive quantifiers are not supported");
}
addQuantifier((Token.Quantifier) token);
} else {
if (quantifier.isSingleChar()) {
throw syntaxErrorHere(JavaErrorMessages.danglingMetaCharacter(quantifier), ErrorCode.InvalidQuantifier);
}
}
break;
case alternation:
astBuilder.nextSequence();
break;
case inlineFlags:
// flagStack push is handled in the lexer
if (!((Token.InlineFlags) token).isGlobal()) {
astBuilder.pushGroup(token);
lexer.pushLocalFlags();
}
lexer.setCurrentFlags((JavaFlags) ((Token.InlineFlags) token).getFlags());View on GitHub (pinned to a66e9ccd1d)
Solutions
- Drop the trailing '+' to make the quantifier greedy: 'a*+' -> 'a*', '\\d++' -> '\\d+'
- If the possessive '+' prevented catastrophic backtracking, redesign the pattern (anchor earlier, use negated classes, unroll the loop) rather than relying on the cut
- Add a pattern pre-check that rejects/maps possessive suffixes when patterns come from users
Example fix
// before String p = "\\d++\\."; // possessive // after String p = "\\d+\\."; // greedy
Defensive patterns
Strategy: validation
Validate before calling
private static final java.util.regex.Pattern POSSESSIVE =
java.util.regex.Pattern.compile("([*+?]|\\{[0-9]+(,[0-9]*)?\\})\\+");
boolean hasPossessiveQuantifier(String pattern) {
return POSSESSIVE.matcher(pattern).find();
} Try / catch
try {
RegexObject re = compileJavaFlavor(pattern);
} catch (UnsupportedRegexException e) {
if (e.getReason().contains("possessive")) {
pattern = POSSESSIVE.matcher(pattern).replaceAll("$1"); // strip possessive '+'
re = compileJavaFlavor(pattern);
} else { throw e; }
} Prevention
- Strip possessive '+' suffixes when translating JDK/PCRE patterns for TRegex
- Test patterns against the target engine in CI, not only against java.util.regex
When it happens
Trigger: Compiling a Java-flavor pattern containing a possessive quantifier, e.g. "a*+", "\\d++\\.", or "[a-z]{2,5}+". The lexer emits a Quantifier token flagged possessive (the extra '+' after the quantifier), and the parser's quantifier case throws when the current term is non-null.
Common situations: Performance-tuned patterns from java.util.regex (possessive quantifiers are a standard JDK feature); ReDoS mitigation advice that suggests *+ or ++; copy-paste from PCRE-centric documentation into a TRegex-backed engine.
Related errors
- possessive quantifiers are not supported
- 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
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/2a4b033325f94648.
Report an issue: GitHub.