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

  1. Use non-capturing groups (?:w1|w2|...) and determine the matched word by re-inspecting the matched text (e.g. a Map lookup).
  2. Split into several patterns each below the cap and run them in sequence.
  3. Replace the regex with a keyword-matching structure (Aho-Corasick, trie, HashSet) when you only need to know which literal matched.
  4. 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

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


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