oracle/graal · error · UnsupportedRegexException

Extended grapheme cluster boundaries are not supported

Error message

Extended grapheme cluster boundaries are not supported

What it means

Thrown by the Java-flavor lexer when a pattern uses \b{g}, the extended grapheme cluster boundary matcher that java.util.regex supports since JDK 21. TRegex compiles patterns to NFAs/DFAs and has no representation for Unicode grapheme cluster boundaries (they require UAX #29 segmentation, not a regular construction). The lexer detects the literal sequence '{g}' right after \b and rejects the pattern at parse time with UnsupportedRegexException.

Source

Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexLexer.java:777

            if (c2 == '\\' && (atEnd() || isEscapeCharClass(curChar()))) {
                throw syntaxError(JavaErrorMessages.ILLEGAL_CHARACTER_RANGE, ErrorCode.InvalidCharacterClass);
            }
            int upper = parseCharClassAtomCodePoint(c2);
            if (upper < ch) {
                throw syntaxError(JavaErrorMessages.ILLEGAL_CHARACTER_RANGE, ErrorCode.InvalidCharacterClass);
            }
            return CodePointSet.create(ch, upper);
        } else {
            return CodePointSet.create(ch);
        }
    }

    @Override
    protected Token parseCustomEscape(char c) {
        switch (c) {
            case 'b' -> {
                if (consumingLookahead("{g}")) {
                    throw new UnsupportedRegexException("Extended grapheme cluster boundaries are not supported");
                }
                return Token.createWordBoundary();
            }
            case 'B' -> {
                return Token.createNonWordBoundary();
            }
            case 'k' -> {
                if (atEnd()) {
                    handleUnfinishedEscape();
                }
                if (consumeChar() != '<') {
                    throw syntaxError(JavaErrorMessages.NAMED_CAPTURE_GROUP_REFERENCE_MISSING_BEGIN, ErrorCode.InvalidBackReference);
                }
                String groupName = javaParseGroupName();
                // backward reference
                if (namedCaptureGroups.containsKey(groupName)) {
                    return Token.createBackReference(getSingleNamedGroupNumber(groupName), false);
                }

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Replace \b{g} with a plain word boundary \b if combining-mark handling is not essential
  2. Approximate a grapheme boundary with (?:\P{M}\p{M}*) or match whole clusters with (?:\P{M}\p{M}*) instead of relying on boundary syntax
  3. Pre-process the pattern string to strip or rewrite \b{g} / \B{g} before handing it to the TRegex-based engine
  4. Route patterns that need true UAX #29 semantics to java.util.regex via java.util.regex.Pattern on a non-host engine, if the embedding allows choosing an engine

Example fix

// before
String p = "\\b{g}\\w+\\b{g}";

// after
String p = "\\b\\w+\\b";
Defensive patterns

Strategy: try-catch

Validate before calling

boolean usesGraphemeBoundary(String pattern) {
    return pattern.contains("\\b{g}") || pattern.contains("\\B{g}");
}

Try / catch

try {
    RegexObject re = compileJavaFlavor(pattern);
} catch (UnsupportedRegexException e) {
    // reason mentions grapheme cluster boundaries; rewrite pattern or switch engine
    pattern = pattern.replace("\\b{g}", "\\b");
}

Prevention

When it happens

Trigger: Compiling a Java-flavor regex (e.g. via RegexCompiler for the Java flavor or GraalVM/Espresso/Truffle host regex) whose pattern contains \b{g} or \B{g}-style grapheme boundary syntax: parsing '\b{g}word\b{g}' reaches parseCustomEscape case 'b', the consumingLookahead("{g}") succeeds, and the exception is thrown immediately.

Common situations: Porting a regex written for JDK 21+ java.util.regex into a GraalVM guest language or TRegex-backed engine; copy-pasting Unicode-aware patterns from Stack Overflow or newer JDK docs; upgrading a codebase from Java 8-17 syntax to Java 21 syntax while the regex engine underneath is TRegex, not java.util.regex.

Related errors


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