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
- Reduce capture groups: convert groups you do not need later to non-capturing (?:...).
- Split the pattern into multiple smaller regexes matched in sequence, each with few groups.
- Narrow alternations inside captured groups (character classes instead of multi-branch alternatives).
- 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
- Use non-capturing groups wherever the captured value is not consumed.
- Keep capture groups and alternation branches correlated: groups * branches below ~3000.
- Split extraction tasks into smaller patterns.
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
- Too much additional capture group tracking overhead
- too many capture group transitions
- dependency cycle
- too many branches in capture group tracking DFA
- DFA transition size explosion
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/a59664b5e6e36770.
Report an issue: GitHub.