{"record":{"id":"cb3927253228d2e9","repo":"oracle/graal","slug":"bounded-quantifier-tracking-would-consume-too-much","errorCode":null,"errorMessage":"Bounded quantifier tracking would consume too much memory at match time: up to %d bytes. Limit: %d bytes","messagePattern":"Bounded quantifier tracking would consume too much memory at match time: up to (.+?) bytes\\. Limit: (.+?) bytes","errorType":"exception","errorClass":"UnsupportedRegexException","httpStatus":null,"severity":"error","filePath":"regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/nodes/dfa/CounterTracker.java","lineNumber":112,"sourceCode":"            int upperBound = Math.max(min, max);\n            CounterTracker tracker;\n            int numberOfCells = trackerSizes[i];\n            if (regressionTestMode) {\n                tracker = new RegressionModeCounterTracker(min, max, numberOfCells, trivialAlwaysReEnter.get(i), trivialNeverReEnter.get(i), dataBuilder);\n            } else if (trivialAlwaysReEnter.get(i)) {\n                tracker = new CounterTrackerTrivialAlwaysReEnter(min, numberOfCells, dataBuilder);\n            } else if (trivialNeverReEnter.get(i)) {\n                tracker = new CounterTrackerTrivialNeverReEnter(min, max, numberOfCells, dataBuilder);\n            } else if (upperBound <= 64) {\n                tracker = new CounterTrackerLong(min, max, numberOfCells, dataBuilder);\n            } else if (upperBound <= 128) {\n                tracker = new CounterTrackerLong2(min, max, numberOfCells, dataBuilder);\n            } else if (upperBound <= 64 * CounterTrackerBitSetWithOffset.MAX_BITSET_SIZE) {\n                tracker = new CounterTrackerBitSetWithOffset(min, max, numberOfCells, dataBuilder);\n            } else {\n                long maxMemoryConsumption = (long) numberOfCells * (long) upperBound * 4;\n                if (maxMemoryConsumption > TRegexOptions.TRegexMaxCounterTrackerMemoryConsumptionInForceLinearExecutionMode) {\n                    throw new UnsupportedRegexException(String.format(\"Bounded quantifier tracking would consume too much memory at match time: up to %d bytes. Limit: %d bytes\", maxMemoryConsumption,\n                                    TRegexOptions.TRegexMaxCounterTrackerMemoryConsumptionInForceLinearExecutionMode));\n                }\n                tracker = new CounterTrackerList(min, max, numberOfCells, dataBuilder);\n            }\n            result[i] = tracker;\n        }\n        return result;\n    }\n\n    /**\n     * Returns true whenever the current state of the counter satisfies the given constraint. The\n     * two data array arguments are the mutable data in which the counters are stored. Note that\n     * this assumes that the constraints concerns the counter tracked by this tracker.\n     */\n    public boolean canExecute(long constraint, long[] fixedData, int[][] intArrays) {\n        int kind = TransitionConstraint.getKind(constraint);\n        int sId = TransitionConstraint.getStateID(constraint);\n        CompilerAsserts.partialEvaluationConstant(constraint);","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/oracle/graal/blob/a66e9ccd1d7bf2552883939aa0788dfd0e294aab/regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/nodes/dfa/CounterTracker.java#L94-L130","documentation":"Thrown while choosing a CounterTracker implementation: for bounded-quantifier counters with upperBound above 64*MAX_BITSET_SIZE, the engine falls back to CounterTrackerList, whose match-time memory is numberOfCells * upperBound * 4 bytes. If that exceeds TRegexMaxCounterTrackerMemoryConsumptionInForceLinearExecutionMode (100 KB), compilation is rejected because the linear executor would allocate too much per match.","triggerScenarios":"Compiling in linear-execution mode a regex with a bounded quantifier whose upper bound is very large (thousands+) combined with enough concurrent counter cells, e.g. (a{0,100000}b){5}, so cells * max * 4 > 102400 bytes.","commonSituations":"Patterns with huge counted bounds like x{1,1000000}; forcing linear execution options (no backtracking fallback) on quantifier-heavy patterns; ported .NET/ICU patterns with very high explicit bounds.","solutions":["Lower the quantifier upper bounds (e.g. replace x{0,100000} with x* plus a length check in code).","Reduce the number of simultaneously active bounded quantifiers.","Do not force linear execution mode — let the engine use the backtracking executor, which needs no counter tracker memory.","Validate the counted bound at runtime instead of in the pattern."],"exampleFix":"// before\nString pattern = \"(a{0,100000}b){5}\";\n\n// after\nString pattern = \"(a*b){5}\"; // then check total length or count in application code","handlingStrategy":"validation","validationCode":"Matcher m = Pattern.compile(\"\\\\{(\\\\d+),(\\\\d+)\\\\}\").matcher(pattern);\nwhile (m.find()) { int max = Integer.parseInt(m.group(2)); if (max > 10_000) throw new IllegalArgumentException(\"quantifier upper bound too high for linear execution (memory cap 100KB)\"); }","typeGuard":"null","tryCatchPattern":"try { compile(pattern); } catch (UnsupportedRegexException e) { if (e.getMessage().contains(\"too much memory\")) { /* lower bounds or use backtracking engine */ } }","preventionTips":["Avoid very large explicit quantifier upper bounds; use * + a length check.","Do not force linear execution mode on quantifier-heavy patterns.","Keep the number of simultaneous bounded quantifiers small."],"tags":["regex","tregex","memory","quantifiers","linear-execution"],"backgroundTag":null,"analyzedSha":"a66e9ccd1d7bf2552883939aa0788dfd0e294aab","analyzedAt":"2026-08-14T13:58:47.161Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}