{"record":{"id":"6ed0c73c20c2ab64","repo":"oracle/graal","slug":"too-many-capture-group-transitions","errorCode":null,"errorMessage":"too many capture group transitions","messagePattern":"too many capture group transitions","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/dfa/DFACaptureGroupTransitionBuilder.java","lineNumber":228,"sourceCode":"            DFAStateNodeBuilder successor = getTarget();\n            DFACaptureGroupPartialTransition[] partialTransitions = new DFACaptureGroupPartialTransition[successor.getSuccessors().length];\n            for (int i = 0; i < successor.getSuccessors().length; i++) {\n                DFACaptureGroupTransitionBuilder successorTransition = (DFACaptureGroupTransitionBuilder) successor.getSuccessors()[i];\n                partialTransitions[i] = createPartialTransition(successorTransition.getRequiredStates(), successorTransition.getRequiredStatesIndexMap(), compilationBuffer);\n            }\n            DFACaptureGroupPartialTransition transitionToFinalState = null;\n            DFACaptureGroupPartialTransition transitionToAnchoredFinalState = null;\n            if (successor.isUnAnchoredFinalState()) {\n                NFAState src = successor.getUnAnchoredFinalStateTransition().getSource();\n                transitionToFinalState = createPartialTransition(StateSet.create(dfaGen.getNfa(), src), StateSetToIntMap.create(src), compilationBuffer);\n            }\n            if (successor.isAnchoredFinalState()) {\n                NFAState src = successor.getAnchoredFinalStateTransition().getSource();\n                transitionToAnchoredFinalState = createPartialTransition(StateSet.create(dfaGen.getNfa(), src), StateSetToIntMap.create(src), compilationBuffer);\n            }\n            assert getId() >= 0;\n            if (getId() > Short.MAX_VALUE) {\n                throw new UnsupportedRegexException(\"too many capture group transitions\");\n            }\n            lazyTransitionBuilder = new DFACaptureGroupLazyTransitionBuilder((short) getId(), partialTransitions, transitionToFinalState, transitionToAnchoredFinalState);\n        }\n        return lazyTransitionBuilder;\n    }\n\n    public static class PartialTransitionDebugInfo implements JsonConvertible {\n\n        private DFACaptureGroupPartialTransition node;\n        private final short[] resultToTransitionMap;\n\n        public PartialTransitionDebugInfo(DFACaptureGroupPartialTransition node) {\n            this(node, 0);\n        }\n\n        public PartialTransitionDebugInfo(int nResults) {\n            this(null, nResults);\n        }","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/dfa/DFACaptureGroupTransitionBuilder.java#L210-L246","documentation":"Thrown by DFACaptureGroupTransitionBuilder when the number of a state's capture-group partial transitions exceeds Short.MAX_VALUE (32767). Partial-transition ids are stored as short in DFACaptureGroupLazyTransitionBuilder, so once getId() > Short.MAX_VALUE the builder refuses to continue and throws UnsupportedRegexException('too many capture group transitions').","triggerScenarios":"Compiling a DFA-backed executor for a pattern whose capture-group tracking creates more than 32767 distinct partial transitions — typically dozens of capture groups combined with alternations or bounded quantifiers ({n,m}) that multiply transition combinations.","commonSituations":"Machine-generated matchers (log parsers, URI/route matchers, lexer specifications) with many capture groups; patterns translated from grammar tools where every rule captures; note that without forceLinearExecution this bailout is usually swallowed and triggers the backtracking fallback, so it mainly surfaces in forced-linear or eager-DFA configurations.","solutions":["Reduce the number of capture groups (use '(?:...)' for groups whose positions you never read)","Simplify alternations that mix many capturing branches; hoist common prefixes out of the capture groups","If you do not need capture positions at all, compile in boolean-match mode or drop the groups entirely","Accept the backtracking fallback: do not force linear execution for these patterns"],"exampleFix":"// before\nString p = \"(a|b|c)(d|e|f)(g|h|i)(j|k|l)...\"; // capturing every branch\n\n// after\nString p = \"[abc][def][ghi][jkl]...\"; // classes instead of capturing branches","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    return compileDfaExecutor(pattern);\n} catch (UnsupportedRegexException e) {\n    if (e.getReason().contains(\"capture group transitions\")) {\n        return compileWithFewerCaptureGroups(pattern); // rewrite (..) -> (?:..) and retry\n    }\n    throw e;\n}","preventionTips":["Keep capture-group counts low in DFA-compiled patterns","Remember these DFA bailouts are normally invisible (automatic backtracking fallback) unless linear execution is forced"],"tags":["regex","tregex","dfa","capture-groups","limits"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}