{"record":{"id":"d249897f6f7c6883","repo":"oracle/graal","slug":"regex-has-unsupported-bounded-quantifier","errorCode":null,"errorMessage":"Regex has unsupported bounded quantifier","messagePattern":"Regex has unsupported bounded quantifier","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/dfa/DFAGenerator.java","lineNumber":1004,"sourceCode":"                    }\n                }\n                for (int i = 0; i < inc.size(); i++) {\n                    inc.get(i).subtract(set1.get(i));\n                    bqTrivialAlwaysReEnter.subtract(inc.get(i));\n                    inc.get(i).clear();\n                    set1.get(i).clear();\n                }\n            }\n        }\n    }\n\n    private static void checkIfAllQuantifierOperationsAreSupported(ObjectArrayBuffer<DFAAbstractNode> nodes, CounterTracker[] counterTrackers) {\n        for (DFAAbstractNode node : nodes) {\n            if (node instanceof DFABQTrackingTransitionOpsNode t) {\n                for (long op : t.getOperations()) {\n                    int qId = TransitionOp.getQuantifierID(op);\n                    if (!counterTrackers[qId].support(op)) {\n                        throw new UnsupportedRegexException(\"Regex has unsupported bounded quantifier\");\n                    }\n                }\n            }\n        }\n    }\n\n    private void createInitialStatesForward() {\n        final int numberOfEntryPoints = nfa.getAnchoredEntry().length;\n        entryStates = new DFAStateNodeBuilder[numberOfEntryPoints * 2];\n        nfa.setInitialLoopBack(isSearching() && !nfa.getAst().getFlags().isSticky());\n        for (int i = 0; i < numberOfEntryPoints; i++) {\n            if (nfa.getAnchoredEntry()[i] == null) {\n                assert nfa.getUnAnchoredEntry()[i] == null;\n                entryStates[i] = null;\n                entryStates[numberOfEntryPoints + i] = null;\n            } else if (nfa.getUnAnchoredEntry()[i] == null) {\n                entryStates[i] = createInitialState(createTransitionBuilder(createNFATransitionSet(nfa.getAnchoredEntry()[i])));\n                entryStates[numberOfEntryPoints + i] = null;","sourceCodeStart":986,"sourceCodeEnd":1022,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/dfa/DFAGenerator.java#L986-L1022","documentation":"Thrown by DFAGenerator.checkIfAllQuantifierOperationsAreSupported when a bounded-quantifier tracking operation is not supported by its counter tracker. Bounded quantifiers are tracked in DFAs with counters; each CounterTracker.support(op) check validates that the counter layout (number of counters, increment/reset semantics needed by the op) is representable. If any op is unsupported, the whole pattern is rejected with UnsupportedRegexException('Regex has unsupported bounded quantifier').","triggerScenarios":"Compiling in DFA mode a pattern whose bounded quantifier combination exceeds the counter-tracker model — e.g. more interleaved bounded quantifiers than the tracker has counters, or quantifier nesting whose increment/reset ordering the chosen tracker cannot express ((a{2,4}b{3,5}){2,3}c{1,2}...).","commonSituations":"Validation-style patterns stacking several {n,m} quantifiers; usually invisible because the default pipeline catches this and falls back to backtracking — it becomes user-visible with forceLinearExecution, eager DFA compilation, or in 'NFA generator bailout' log lines.","solutions":["Reduce the number of distinct bounded quantifiers in the pattern, especially interleaved/nested ones","Replace inner bounded quantifiers with unbounded '+' or '*' plus explicit count validation after matching","Unroll small fixed repetitions literally (a{3} -> aaa) where feasible","Let the engine fall back to backtracking (do not force linear execution) if the pattern is trusted"],"exampleFix":"// before\nString p = \"(\\\\d{2,4}-){2,3}\\\\d{1,2}\"; // several interleaved bounded quantifiers\n\n// after\nString p = \"(\\\\d+-){2,3}\\\\d+\"; // then verify digit counts in code","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    return compile(pattern, forceLinearOptions);\n} catch (UnsupportedRegexException e) {\n    if (e.getReason().contains(\"bounded quantifier\")) {\n        return compile(unrollBoundedQuantifiers(pattern), forceLinearOptions);\n    }\n    throw e;\n}","preventionTips":["Limit the count of interleaved {n,m} quantifiers per pattern","Unroll small fixed repetitions or validate counts post-match"],"tags":["regex","tregex","dfa","bounded-quantifier","counter-tracking"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}