{"record":{"id":"71d0c1c442be5bef","repo":"oracle/graal","slug":"end-of-previous-match-boundary-matcher-is-not-supp","errorCode":null,"errorMessage":"End of previous match boundary matcher is not supported","messagePattern":"End of previous match boundary matcher is not supported","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexLexer.java","lineNumber":802,"sourceCode":"            case 'k' -> {\n                if (atEnd()) {\n                    handleUnfinishedEscape();\n                }\n                if (consumeChar() != '<') {\n                    throw syntaxError(JavaErrorMessages.NAMED_CAPTURE_GROUP_REFERENCE_MISSING_BEGIN, ErrorCode.InvalidBackReference);\n                }\n                String groupName = javaParseGroupName();\n                // backward reference\n                if (namedCaptureGroups.containsKey(groupName)) {\n                    return Token.createBackReference(getSingleNamedGroupNumber(groupName), false);\n                }\n                throw syntaxError(JavaErrorMessages.unknownGroupReference(groupName), ErrorCode.InvalidBackReference);\n            }\n            case 'R' -> {\n                return Token.createLineBreak();\n            }\n            case 'X' -> throw new UnsupportedRegexException(\"Grapheme clusters are not supported\");\n            case 'G' -> throw new UnsupportedRegexException(\"End of previous match boundary matcher is not supported\");\n            case 'Q' -> {\n                int start = position;\n                int end = pattern.indexOf(\"\\\\E\", start);\n                if (end < 0) {\n                    end = pattern.length();\n                    position = end;\n                } else {\n                    position = end + 2;\n                }\n                return Token.createLiteralString(start, end);\n            }\n        }\n        return null;\n    }\n\n    @Override\n    protected int parseCustomEscapeChar(char c, boolean inCharClass) {\n        switch (c) {","sourceCodeStart":784,"sourceCodeEnd":820,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexLexer.java#L784-L820","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nString p = \"\\\\G\\\\w+\"; // tokenizer relying on \\\\G\n\n// after\nString p = \"\\\\w+\";\n// enforce contiguity in the loop:\n// if (m.end() != pos) break; else pos = m.end();","handlingStrategy":"try-catch","validationCode":"boolean usesGAnchor(String pattern) {\n    return pattern.contains(\"\\\\G\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    RegexObject re = compileJavaFlavor(pattern);\n} catch (UnsupportedRegexException e) {\n    if (pattern.contains(\"\\\\G\")) {\n        pattern = pattern.replace(\"\\\\G\", \"\"); // enforce contiguity in the match loop instead\n    }\n}","preventionTips":["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"],"tags":["regex","java-flavor","anchor","tokenizer","unsupported-feature"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}