oracle/graal · error · UnsupportedRegexException

Bounded quantifier tracking would consume too much memory at

Error message

Bounded quantifier tracking would consume too much memory at match time: up to %d bytes. Limit: %d bytes

What it means

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.

Source

Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/nodes/dfa/CounterTracker.java:112

            int upperBound = Math.max(min, max);
            CounterTracker tracker;
            int numberOfCells = trackerSizes[i];
            if (regressionTestMode) {
                tracker = new RegressionModeCounterTracker(min, max, numberOfCells, trivialAlwaysReEnter.get(i), trivialNeverReEnter.get(i), dataBuilder);
            } else if (trivialAlwaysReEnter.get(i)) {
                tracker = new CounterTrackerTrivialAlwaysReEnter(min, numberOfCells, dataBuilder);
            } else if (trivialNeverReEnter.get(i)) {
                tracker = new CounterTrackerTrivialNeverReEnter(min, max, numberOfCells, dataBuilder);
            } else if (upperBound <= 64) {
                tracker = new CounterTrackerLong(min, max, numberOfCells, dataBuilder);
            } else if (upperBound <= 128) {
                tracker = new CounterTrackerLong2(min, max, numberOfCells, dataBuilder);
            } else if (upperBound <= 64 * CounterTrackerBitSetWithOffset.MAX_BITSET_SIZE) {
                tracker = new CounterTrackerBitSetWithOffset(min, max, numberOfCells, dataBuilder);
            } else {
                long maxMemoryConsumption = (long) numberOfCells * (long) upperBound * 4;
                if (maxMemoryConsumption > TRegexOptions.TRegexMaxCounterTrackerMemoryConsumptionInForceLinearExecutionMode) {
                    throw new UnsupportedRegexException(String.format("Bounded quantifier tracking would consume too much memory at match time: up to %d bytes. Limit: %d bytes", maxMemoryConsumption,
                                    TRegexOptions.TRegexMaxCounterTrackerMemoryConsumptionInForceLinearExecutionMode));
                }
                tracker = new CounterTrackerList(min, max, numberOfCells, dataBuilder);
            }
            result[i] = tracker;
        }
        return result;
    }

    /**
     * Returns true whenever the current state of the counter satisfies the given constraint. The
     * two data array arguments are the mutable data in which the counters are stored. Note that
     * this assumes that the constraints concerns the counter tracked by this tracker.
     */
    public boolean canExecute(long constraint, long[] fixedData, int[][] intArrays) {
        int kind = TransitionConstraint.getKind(constraint);
        int sId = TransitionConstraint.getStateID(constraint);
        CompilerAsserts.partialEvaluationConstant(constraint);

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Lower the quantifier upper bounds (e.g. replace x{0,100000} with x* plus a length check in code).
  2. Reduce the number of simultaneously active bounded quantifiers.
  3. Do not force linear execution mode — let the engine use the backtracking executor, which needs no counter tracker memory.
  4. Validate the counted bound at runtime instead of in the pattern.

Example fix

// before
String pattern = "(a{0,100000}b){5}";

// after
String pattern = "(a*b){5}"; // then check total length or count in application code
Defensive patterns

Strategy: validation

Validate before calling

Matcher m = Pattern.compile("\\{(\\d+),(\\d+)\\}").matcher(pattern);
while (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)"); }

Type guard

null

Try / catch

try { compile(pattern); } catch (UnsupportedRegexException e) { if (e.getMessage().contains("too much memory")) { /* lower bounds or use backtracking engine */ } }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/cb3927253228d2e9. Report an issue: GitHub.