oracle/graal · error · UnsupportedRegexException
End of previous match boundary matcher is not supported
Error message
End of previous match boundary matcher is not supported
What it means
Thrown by the Java-flavor lexer for \G, the 'end of previous match' anchor. \G is stateful: it depends on where the previous invocation of the matcher ended, and TRegex executors are stateless per-match compiled automata with no notion of a resumable match position handed back into the pattern, so case 'G' rejects it with UnsupportedRegexException.
Source
Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexLexer.java:802
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);
}
throw syntaxError(JavaErrorMessages.unknownGroupReference(groupName), ErrorCode.InvalidBackReference);
}
case 'R' -> {
return Token.createLineBreak();
}
case 'X' -> throw new UnsupportedRegexException("Grapheme clusters are not supported");
case 'G' -> throw new UnsupportedRegexException("End of previous match boundary matcher is not supported");
case 'Q' -> {
int start = position;
int end = pattern.indexOf("\\E", start);
if (end < 0) {
end = pattern.length();
position = end;
} else {
position = end + 2;
}
return Token.createLiteralString(start, end);
}
}
return null;
}
@Override
protected int parseCustomEscapeChar(char c, boolean inCharClass) {
switch (c) {View on GitHub (pinned to a66e9ccd1d)
Solutions
- Remove \G and enforce contiguity in the calling code: check matcher.start() == expectedPos after each match instead of anchoring in the pattern
- Replace \G with \A when you actually only ever match from the absolute beginning of a sliced input region
- Use java.util.regex directly for tokenizer patterns that depend on \G, if the embedding permits choosing the engine
Example fix
// before String p = "\\G\\w+"; // tokenizer relying on \\G // after String p = "\\w+"; // enforce contiguity in the loop: // if (m.end() != pos) break; else pos = m.end();
Defensive patterns
Strategy: try-catch
Validate before calling
boolean usesGAnchor(String pattern) {
return pattern.contains("\\G");
} Try / catch
try {
RegexObject re = compileJavaFlavor(pattern);
} catch (UnsupportedRegexException e) {
if (pattern.contains("\\G")) {
pattern = pattern.replace("\\G", ""); // enforce contiguity in the match loop instead
}
} Prevention
- Implement tokenizer contiguity checks in host code (compare matcher end to expected position) instead of \G
- Do not copy \G-based tokenizer patterns from java.util.regex into TRegex-backed engines
When it happens
Trigger: Compiling a Java-flavor pattern containing \G, typically for tokenizing input in a loop, e.g. "\\G\\w+" or "\\Gitem(?:,|\\z)". parseCustomEscape case 'G' throws immediately at lex time.
Common situations: Ported tokenizer loops from java.util.regex, PHP preg_match_all, or .NET Regex where \G enforces contiguous token matching; developers moving tokenizer code into a GraalVM guest language or any embedding backed by TRegex.
Related errors
- Extended grapheme cluster boundaries are not supported
- Grapheme clusters are 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/71d0c1c442be5bef.
Report an issue: GitHub.