{"record":{"id":"55ce6a0c8fc673b3","repo":"oracle/graal","slug":"independent-non-capturing-groups-are-not-supported","errorCode":null,"errorMessage":"Independent non-capturing groups are not supported","messagePattern":"Independent non-capturing groups 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":910,"sourceCode":"\n    @Override\n    protected int parseCustomEscapeCharFallback(int c, boolean inCharClass) {\n        // any non-alphabetic character can be used after an escape\n        // digits are not accepted here since they should have been parsed as octal sequence or\n        // backreference earlier\n        if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) {\n            throw syntaxError(JavaErrorMessages.ILLEGAL_ESCAPE_SEQUENCE, ErrorCode.InvalidEscape);\n        }\n        return c;\n    }\n\n    @Override\n    protected Token parseCustomGroupBeginQ(char charAfterQuestionMark) {\n        // TODO do we need to do something else than inline flags? check \"Special constructs\" in\n        // documentation\n        char c = charAfterQuestionMark;\n        if (c == '>') {\n            throw new UnsupportedRegexException(\"Independent non-capturing groups are not supported\");\n        }\n        int firstCharPos = position;\n        JavaFlags newFlags = getLocalFlags();\n        while (JavaFlags.isValidFlagChar(c)) {\n            newFlags = newFlags.addFlag(c);\n            if (atEnd()) {\n                throw handleUnfinishedGroupQ();\n            }\n            c = consumeChar();\n        }\n        if (c == '-') {\n            if (atEnd()) {\n                throw handleUnfinishedGroupQ();\n            }\n            c = consumeChar();\n            while (JavaFlags.isValidFlagChar(c)) {\n                newFlags = newFlags.delFlag(c);\n                if (atEnd()) {","sourceCodeStart":892,"sourceCodeEnd":928,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/flavor/java/JavaRegexLexer.java#L892-L928","documentation":"Thrown by the Java-flavor lexer for the atomic/independent group construct (?>...), which prevents backtracking into the group once it has matched. TRegex's compilation pipeline has no mechanism to seal a subexpression against backtracking (its executors either match purely or backtrack exhaustively), so parseCustomGroupBeginQ rejects the '>' after '(?' with UnsupportedRegexException.","triggerScenarios":"Compiling any Java-flavor pattern containing (?> ... ), e.g. \"(?>ab)+c\" or possessive-style optimizations like \"(?>\\\\d+)\\\\.\". The lexer sees '(' then '?' then '>' in parseCustomGroupBeginQ and throws before parsing group contents.","commonSituations":"ReDoS-hardening guides recommend atomic groups; developers copy PCRE/Perl/.NET patterns using (?>...) for performance; patterns migrated from Rust/RE2-style possessive notation into a TRegex-backed engine.","solutions":["Replace (?>X) with a plain non-capturing group (?:X) and accept the backtracking semantics difference","If atomicity guarded against catastrophic backtracking, restructure the pattern (e.g. unroll loops, use negated character classes) to bound the search instead","Verify correctness after the rewrite: (?:X)+ and (?>X)+ can produce different matches on partial-match inputs"],"exampleFix":"// before\nString p = \"(?>\\\\d+)\\\\.\";\n\n// after\nString p = \"(?:\\\\d+)\\\\.\";","handlingStrategy":"validation","validationCode":"boolean usesAtomicGroup(String pattern) {\n    return pattern.contains(\"(?>\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    RegexObject re = compileJavaFlavor(pattern);\n} catch (UnsupportedRegexException e) {\n    pattern = pattern.replace(\"(?>\", \"(?:\"); // semantics differ: verify matches still correct\n    re = compileJavaFlavor(pattern);\n}","preventionTips":["Lint incoming patterns for '(?>' before compiling on TRegex","Prefer structural ReDoS defenses (anchors, negated classes) over atomic groups for portable patterns"],"tags":["regex","java-flavor","atomic-group","backtracking","unsupported-feature"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}