{"record":{"id":"a59664b5e6e36770","repo":"oracle/graal","slug":"too-many-partial-transitions","errorCode":null,"errorMessage":"too many partial transitions","messagePattern":"too many partial transitions","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/Counter.java","lineNumber":91,"sourceCode":"        count -= i;\n        return ret;\n    }\n\n    public static class ThresholdCounter extends Counter {\n\n        private final int max;\n        private final String errorMsg;\n\n        public ThresholdCounter(int max, String errorMsg) {\n            this.max = max;\n            this.errorMsg = errorMsg;\n        }\n\n        @Override\n        public int inc(int i) {\n            final int ret = super.inc(i);\n            if (getCount() > max) {\n                throw new UnsupportedRegexException(errorMsg);\n            }\n            return ret;\n        }\n    }\n\n    public static class ThreadSafeCounter extends Counter {\n\n        @Override\n        public int inc() {\n            int c = count;\n            if (c < Integer.MAX_VALUE) {\n                count = c + 1;\n            }\n            return count;\n        }\n\n        @Override\n        public int inc(int i) {","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/Counter.java#L73-L109","documentation":"Thrown by Counter.ThresholdCounter.inc (message supplied by the caller) — in practice by DFAGenerator's cgPartialTransitionIDCounter, which caps DFA capture-group partial transitions at TRegexMaxDFACGPartialTransitions (3000). Capture-group DFAs build one partial-transition node per distinct NFA-state-set; regexes with many capture groups and alternations can multiply these beyond the cap.","triggerScenarios":"Compiling in DFA-with-capture-groups mode a pattern whose number of distinct partial transitions (per combinations of active NFA states in transitions) exceeds 3000 — typically many capture groups spread over wide alternations, e.g. (a)(b|c)(d|e)... repeated.","commonSituations":"Extraction patterns with dozens of capture groups over branching alternatives; generated patterns assigning a capture group to every token; exceeding the cap after adding 'just one more' group to a long-lived pattern.","solutions":["Reduce capture groups: convert groups you do not need later to non-capturing (?:...).","Split the pattern into multiple smaller regexes matched in sequence, each with few groups.","Narrow alternations inside captured groups (character classes instead of multi-branch alternatives).","Let the engine fall back to the backtracking executor instead of forcing the capture-group DFA."],"exampleFix":"// before\nString pattern = \"(a)(b|c)(d|e)(f|g)(h|i)...\"; // many capturing branches\n\n// after\nString pattern = \"a[bc][de][fg][hi]...\"; // non-capturing classes, groups only where values are needed","handlingStrategy":"fallback","validationCode":"int groups = 0; for (int i = 0; i + 1 < pattern.length(); i++) { if (pattern.charAt(i) == '(' && pattern.charAt(i + 1) != '?') groups++; } if (groups > 50) throw new IllegalArgumentException(\"many capture groups over alternations; partial-transition limit (3000) risk\");","typeGuard":"null","tryCatchPattern":"try { compileDfa(pattern); } catch (UnsupportedRegexException e) { compileBacktracking(pattern); } // do not force the capture-group DFA on group-heavy patterns","preventionTips":["Use non-capturing groups wherever the captured value is not consumed.","Keep capture groups and alternation branches correlated: groups * branches below ~3000.","Split extraction tasks into smaller patterns."],"tags":["regex","tregex","dfa","capture-groups","limit-exceeded"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}