oracle/graal · error · UnsupportedRegexException

too many partial transitions

Error message

too many partial transitions

What it means

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.

Source

Thrown at regex/src/com.oracle.truffle.regex/src/com/oracle/truffle/regex/tregex/parser/Counter.java:91

        count -= i;
        return ret;
    }

    public static class ThresholdCounter extends Counter {

        private final int max;
        private final String errorMsg;

        public ThresholdCounter(int max, String errorMsg) {
            this.max = max;
            this.errorMsg = errorMsg;
        }

        @Override
        public int inc(int i) {
            final int ret = super.inc(i);
            if (getCount() > max) {
                throw new UnsupportedRegexException(errorMsg);
            }
            return ret;
        }
    }

    public static class ThreadSafeCounter extends Counter {

        @Override
        public int inc() {
            int c = count;
            if (c < Integer.MAX_VALUE) {
                count = c + 1;
            }
            return count;
        }

        @Override
        public int inc(int i) {

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Reduce capture groups: convert groups you do not need later to non-capturing (?:...).
  2. Split the pattern into multiple smaller regexes matched in sequence, each with few groups.
  3. Narrow alternations inside captured groups (character classes instead of multi-branch alternatives).
  4. Let the engine fall back to the backtracking executor instead of forcing the capture-group DFA.

Example fix

// before
String pattern = "(a)(b|c)(d|e)(f|g)(h|i)..."; // many capturing branches

// after
String pattern = "a[bc][de][fg][hi]..."; // non-capturing classes, groups only where values are needed
Defensive patterns

Strategy: fallback

Validate before calling

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");

Type guard

null

Try / catch

try { compileDfa(pattern); } catch (UnsupportedRegexException e) { compileBacktracking(pattern); } // do not force the capture-group DFA on group-heavy patterns

Prevention

When it happens

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

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

Related errors


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