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
- Replace \b{g} with a plain word boundary \b if combining-mark handling is not essential
- Approximate a grapheme boundary with (?:\P{M}\p{M}*) or match whole clusters with (?:\P{M}\p{M}*) instead of relying on boundary syntax
- Pre-process the pattern string to strip or rewrite \b{g} / \B{g} before handing it to the TRegex-based engine
- 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 porting JDK 21+ regexes, scan for \b{g}/\B{g} before compiling on TRegex
- Keep a list of known-unsupported Java-flavor constructs (\X, \G, (?>, possessive quantifiers) in your pattern linting
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
- Grapheme clusters are not supported
- End of previous match boundary matcher is not supported
- Literal escape not supported in this context
- Independent non-capturing groups are not supported
- possessive quantifiers are not supported
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/609ca2f778038da0.
Report an issue: GitHub.