{"record":{"id":"df85b285f2012b13","repo":"oracle/graal","slug":"grapheme-clusters-are-not-supported","errorCode":null,"errorMessage":"Grapheme clusters are not supported","messagePattern":"Grapheme clusters are 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":801,"sourceCode":"            }\n            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) {","sourceCodeStart":783,"sourceCodeEnd":819,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexLexer.java#L783-L819","documentation":"Thrown by the Java-flavor lexer when the pattern contains \\X, the 'grapheme cluster' escape (any extended grapheme cluster, roughly one user-perceived character). TRegex has no AST node or NFA construction for unbounded grapheme clustering because it needs Unicode segmentation rather than a regular expression over code points, so case 'X' in parseCustomEscape rejects it outright with UnsupportedRegexException.","triggerScenarios":"Any Java-flavor pattern containing the two-character escape \\X, e.g. compiling \"\\\\X+\" or \"\\\\b\\\\X*\\\\b\". The lexer hits case 'X' in parseCustomEscape and throws before any parser or DFA stage runs.","commonSituations":"Patterns ported from PCRE, Perl, Ruby, or .NET where \\X is idiomatic for Unicode text segmentation; emoji/CJK processing regexes copied from other languages into a TRegex-backed Java engine; older patterns that used (?:\\r\\n|.) and get 'modernized' to \\X.","solutions":["Replace \\X with a base-plus-marks approximation such as (?:\\P{M}\\p{M}*)","If only line-oriented matching was intended, use . with DOTALL or an explicit (?:\\r\\n|[\\s\\S]) alternation","Do heavyweight grapheme segmentation with a dedicated Unicode library (ICU BreakIterator) outside the regex, matching each segment with a simple regex"],"exampleFix":"// before\nString p = \"\\\\X\"; // one grapheme cluster\n\n// after\nString p = \"(?:\\\\P{M}\\\\p{M}*)\"; // base char + combining marks","handlingStrategy":"try-catch","validationCode":"boolean usesGraphemeEscape(String pattern) {\n    return pattern.contains(\"\\\\X\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    RegexObject re = compileJavaFlavor(pattern);\n} catch (UnsupportedRegexException e) {\n    pattern = pattern.replace(\"\\\\X\", \"(?:\\\\P{M}\\\\p{M}*)\");\n    re = compileJavaFlavor(pattern);\n}","preventionTips":["Avoid \\X in patterns meant to run on TRegex; use (?:\\P{M}\\p{M}*) instead","For exact grapheme segmentation use ICU BreakIterator outside the regex"],"tags":["regex","java-flavor","unicode","grapheme-cluster","unsupported-feature"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}