{"record":{"id":"be664496cdbc100d","repo":"oracle/graal","slug":"case-unfolding-of-case-insensitive-string-is-too-c","errorCode":null,"errorMessage":"case-unfolding of case-insensitive string is too complex","messagePattern":"case-unfolding of case-insensitive string is too complex","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/MultiCharacterCaseFolding.java","lineNumber":171,"sourceCode":"    private static List<Integer> caseFold(CaseFoldData.CaseFoldAlgorithm algorithm, int[] codepoints) {\n        List<Integer> caseFolded = new ArrayList<>();\n        for (int codepoint : codepoints) {\n            int[] folded = caseFold(algorithm, codepoint);\n            if (folded == null) {\n                caseFolded.add(codepoint);\n            } else {\n                for (int foldedElem : folded) {\n                    caseFolded.add(foldedElem);\n                }\n            }\n        }\n        return caseFolded;\n    }\n\n    private static void unfoldSegment(CaseFoldData.CaseFoldAlgorithm algorithm, RegexASTBuilder astBuilder, ArrayList<OracleDBCharClassTrieNode> leafNodes, List<Integer> caseFolded,\n                    List<Unfolding> unfoldings, int start, int end, int backtrackingDepth, boolean dropAsciiOnStart, boolean transitiveEquivalence, CompilationBuffer compilationBuffer) {\n        if (backtrackingDepth > 12) {\n            throw new UnsupportedRegexException(\"case-unfolding of case-insensitive string is too complex\");\n        }\n        // The terminating condition of this recursion. This is reached when we have generated\n        // an alternative that covers the entire case-folded segment given by `start` and `end`.\n        if (start == end) {\n            return;\n        }\n        // This shouldn't happen in our current use case, but it's included for completeness.\n        if (unfoldings.isEmpty()) {\n            addString(astBuilder, leafNodes, caseFolded.subList(start, end), compilationBuffer);\n            return;\n        }\n        Unfolding unfolding = unfoldings.get(0);\n        // Fast-forward to the next possible unfolding.\n        if (unfolding.getStart() > start) {\n            addString(astBuilder, leafNodes, caseFolded.subList(start, unfolding.getStart()), compilationBuffer);\n            unfoldSegment(algorithm, astBuilder, leafNodes, caseFolded, unfoldings, unfolding.getStart(), end, backtrackingDepth, dropAsciiOnStart, transitiveEquivalence, compilationBuffer);\n            return;\n        }","sourceCodeStart":153,"sourceCodeEnd":189,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/MultiCharacterCaseFolding.java#L153-L189","documentation":"Thrown by MultiCharacterCaseFolding.unfoldSegment when the recursion depth of case-unfolding a case-insensitive string segment exceeds 12. To match case-insensitively against all foldings (e.g. 'ss' vs U+00DF, Greek sigma variants, ligatures), the parser enumerates reverse unfoldings with backtracking; segments whose alternatives multiply beyond depth 12 are rejected as too complex.","triggerScenarios":"Compiling a case-insensitive literal string (or string-containing character class, e.g. via \\q{...} or set strings) that contains characters with many case-folding equivalences in close succession, e.g. long runs of 's', 'i', 'k', 'sigma' or ligature characters under CASE_INSENSITIVE (especially with Unicode case folding).","commonSituations":"Case-insensitive matching of German/Greek/Turkish text or ligature-rich input; flavor-specific string sets (OracleDB char-class tries) with case-insensitive flags; patterns generated from user text with many fold-prone letters in a row.","solutions":["Shorten the literal run of fold-ambiguous characters, or split it so each segment has fewer consecutive folding alternatives.","Restrict case-insensitivity to the ASCII part, or drop CASE_INSENSITIVE for that segment and normalize input case in application code instead.","Match case-sensitively against pre-lowercased input (and pre-lowercased pattern) when full Unicode folding is not required."],"exampleFix":"// before\nPattern p = Pattern.compile(\"\\u00dfss\\u00dfss\\u00dfss\", Pattern.CASE_INSENSITIVE); // deep unfolding\n\n// after\nPattern p = Pattern.compile(Pattern.quote(input.toLowerCase()), Pattern.CASE_INSENSITIVE); // or match case-sensitively on normalized text","handlingStrategy":"validation","validationCode":"// count fold-ambiguous characters in case-insensitive literals before compiling\nString foldProne = \"s\\u00df\\u03c3\\u03c2i\\u0130k\\u00f6\"; long n = literal.chars().filter(c -> foldProne.indexOf(c) >= 0).count(); if (n > 12) throw new IllegalArgumentException(\"case-unfolding too complex\");","typeGuard":"null","tryCatchPattern":"try { compileCaseInsensitive(pattern); } catch (UnsupportedRegexException e) { /* match case-sensitively on pre-normalized text instead */ }","preventionTips":["Normalize case of pattern and input yourself instead of CASE_INSENSITIVE for fold-heavy text.","Shorten literal runs of fold-ambiguous characters.","Restrict case-insensitive matching to ASCII ranges where possible."],"tags":["regex","tregex","case-insensitive","unicode","parser"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}