oracle/graal · error · UnsupportedRegexException
too many capture groups
Error message
too many capture groups
What it means
Thrown by Counter.ThresholdCounter.inc with message 'too many capture groups' — instantiated as RegexAST.groupCount, capped at TRegexOptions.TRegexMaxNumberOfCaptureGroups (Short.MAX_VALUE = 32767). Group numbers are stored in a short across the AST and engine data structures, so declaring group number 32768 aborts parsing.
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
- Use non-capturing groups (?:w1|w2|...) and determine the matched word by re-inspecting the matched text (e.g. a Map lookup).
- Split into several patterns each below the cap and run them in sequence.
- Replace the regex with a keyword-matching structure (Aho-Corasick, trie, HashSet) when you only need to know which literal matched.
- Add a generation-time guard counting '(' to fail before compiling.
Example fix
// before
String pattern = words.stream().map(w -> "(" + Pattern.quote(w) + ")").collect(Collectors.joining("|"));
// after
String pattern = "(?:" + words.stream().map(Pattern::quote).collect(Collectors.joining("|")) + ")";
String matched = m.group(); // then map back to the word via a Map<String,String> Defensive patterns
Strategy: validation
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 > 32_000) throw new IllegalArgumentException("too many capture groups (max 32767)"); Type guard
null
Try / catch
try { compile(pattern); } catch (UnsupportedRegexException e) { if (e.getMessage().contains("capture groups")) { /* switch groups to non-capturing and recover matched text via lookup */ } } Prevention
- Default to (?:...) and add capturing groups only for values you actually read.
- Recover 'which alternative matched' via a Map on m.group() instead of one group per branch.
- Enforce a capture-group budget in pattern generators.
When it happens
Trigger: Compiling a pattern with more than 32767 capturing groups, e.g. a generated pattern that wraps every element of a large list in parentheses ((w1)|(w2)|...|(w40000)).
Common situations: Keyword-search patterns built by wrapping each word in a capture group to learn which one matched; generated extraction patterns from schemas or templates; word lists that grow past the threshold over time.
Related errors
- Class set expression maximum nesting level exceeded
- too many sequences in a single group
- too many terms in a single sequence
- too many partial transitions
- Too much additional capture group tracking overhead
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/6e5227f6d394a1dc.
Report an issue: GitHub.