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

  1. Drop the trailing '+' to make the quantifier greedy: 'a*+' -> 'a*', '\\d++' -> '\\d+'
  2. If the possessive '+' prevented catastrophic backtracking, redesign the pattern (anchor earlier, use negated classes, unroll the loop) rather than relying on the cut
  3. 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

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


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/2a4b033325f94648. Report an issue: GitHub.