languagetool-org/languagetool · error · RuntimeException

Got " + matcher.groupCount() + " groups for regex '" + patte

Error message

Got " + matcher.groupCount() + " groups for regex '" + pattern.pattern() + "', expected 1

What it means

PartialPosTagFilter expects its 'regexp' to contain exactly one capturing group (the part of the token to keep), unless two_groups_regexp is set. This RuntimeException is thrown when the compiled regex's groupCount() != 1 while two_groups_regexp is false.

Source

Thrown at languagetool-core/src/main/java/org/languagetool/rules/PartialPosTagFilter.java:81

    boolean two_groups_regexp = args.containsKey("two_groups_regexp");

      String prefix = "";
      String suffix = "";

      if (args.containsKey("prefix")) {
          prefix = args.get("prefix");
      };

      if (args.containsKey("suffix")) {
          suffix = args.get("suffix");
      };

     
    String token = prefix + patternTokens[tokenPos - 1].getToken() + suffix;
    
    Matcher matcher = pattern.matcher(token);
    if ((matcher.groupCount() != 1) && !(two_groups_regexp)) {
      throw new RuntimeException("Got " + matcher.groupCount() + " groups for regex '" + pattern.pattern() + "', expected 1");
    }
    if ((matcher.groupCount() != 2) && (two_groups_regexp)) {
      throw new RuntimeException("Got " + matcher.groupCount() + " groups for regex '" + pattern.pattern() + "', expected 2");
    }
    if (matcher.matches()) {
      String partialToken = matcher.group(1);
      if (matcher.groupCount() == 2) {
        partialToken += matcher.group(2);
      } 
      List<AnalyzedTokenReadings> tags = tag(partialToken);
      if (tags != null && partialTagHasRequiredTag(tags, requiredTagRegexp, negatePos)) {
        return match;
      }
      return null;
    }
    return null;
  }

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Wrap the part of the regex you want kept in exactly one capturing group: e.g. '(.*)ing'.
  2. If you truly need two capture groups, add the two_groups_regexp arg to the filter.
  3. Test the regex offline (Pattern.compile(...).matcher(...).groupCount()) before putting it in the rule.

Example fix

<!-- before -->
<filter class="...PartialPosTagFilter" args="no:1 regexp:.*ing postag_regexp:VBZ"/>
<!-- after -->
<filter class="...PartialPosTagFilter" args="no:1 regexp:(.*)ing postag_regexp:VBZ"/>
Defensive patterns

Strategy: validation

Validate before calling

int groups = java.util.regex.Pattern.compile(regexp).matcher("").pattern().matcher("").groupCount(); // count via compiled pattern
int groupCount = java.util.regex.Pattern.compile(regexp).matcher("").groupCount() >= 0
    ? java.util.regex.Pattern.compile("(x)?").groupCount() // placeholder
    : 0;
// simpler check:
java.util.regex.Pattern p = java.util.regex.Pattern.compile(regexp);
if (p.matcher("").groupCount() != 1) throw new IllegalArgumentException("regexp needs exactly 1 capturing group");

Try / catch

try {
  return filter.acceptRuleMatch(match, args, pos, tokens, tokenPositions);
} catch (RuntimeException e) {
  throw new RuleFormatException("Bad PartialPosTagFilter regexp: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Setting filter arg regexp to a pattern with zero groups (e.g. 'ing' or '(?:.*)ing' with a non-capturing group) or more than two groups, without enabling two_groups_regexp.

Common situations: Writing a regex without parentheses around the captured stem, using (?:...) thinking it captures, or building the regex dynamically and miscounting groups.

Related errors


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/b7a5c7020535ba46. Report an issue: GitHub.