languagetool-org/languagetool · error · RuntimeException

Rule group

Error message

Rule group 

What it means

In pattern-rule XML, minPrevMatches may be set on a <rulegroup> or on individual <rule> elements, not both in a conflicting way. If a rule declares minPrevMatches while its rule group already set it to a value > 0, PatternRuleHandler.startElement throws this RuntimeException to prevent ambiguous inheritance.

Source

Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java:179

        inRegex = true;
        regexMode = "exact".equals(attrs.getValue("type")) ? RegexpMode.EXACT : RegexpMode.SMART;
        regexCaseSensitive = attrs.getValue(CASE_SENSITIVE) != null && YES.equals(attrs.getValue(CASE_SENSITIVE));
        regexpMark = attrs.getValue(MARK) != null ? Integer.parseInt(attrs.getValue(MARK)) : 0;
        break;
      case RULE:
        xmlLineNumber = pLocator.getLineNumber();
        regex = new StringBuilder();
        inRule = true;
        shortMessage = new StringBuilder();
        message = new StringBuilder();
        suggestionsOutMsg = new StringBuilder();
        url = new StringBuilder();
        id = attrs.getValue(ID);
        name = attrs.getValue(NAME);
        String minPrevMatchesStr = attrs.getValue(MINPREVMATCHES);
        if (minPrevMatchesStr != null) {
          if (inRuleGroup && ruleGroupMinPrevMatches > 0) {
            throw new RuntimeException("Rule group " + ruleGroupId + " has " + MINPREVMATCHES + "=" + ruleGroupMinPrevMatches
                + ", thus rule " + id + " cannot specify " + MINPREVMATCHES);
          }
          minPrevMatches = Integer.parseInt(minPrevMatchesStr);  
        } else {
          minPrevMatches = ruleGroupMinPrevMatches;
        }
        String distanceTokensStr = attrs.getValue(DISTANCETOKENS);
        if (distanceTokensStr != null) {
          if (inRuleGroup && ruleGroupDistanceTokens > 0) {
            throw new RuntimeException("Rule group " + ruleGroupId + " has " + DISTANCETOKENS + "=" + ruleGroupDistanceTokens
                + ", thus rule " + id + " cannot specify " + DISTANCETOKENS);
          }
          distanceTokens = Integer.parseInt(distanceTokensStr);  
        } else {
          distanceTokens = ruleGroupDistanceTokens;
        }
        String premiumRule = attrs.getValue(PREMIUM);
        //check if this rule is premium

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Remove the minPrevMatches attribute from the inner <rule> and rely on the rulegroup value
  2. Or remove it from the <rulegroup> if the per-rule values are intended to differ
  3. Search the XML for rules inside groups that both define minPrevMatches and resolve the duplication

Example fix

// before
<rulegroup id="G" minPrevMatches="2">
  <rule id="R1" minPrevMatches="1">...
// after
<rulegroup id="G" minPrevMatches="2">
  <rule id="R1">...
Defensive patterns

Strategy: validation

Validate before calling

if (ruleGroupMinPrevMatches > 0 && rule.hasAttribute("minPrevMatches")) {
  throw new IllegalArgumentException("minPrevMatches set on both rulegroup and rule");
}

Try / catch

try {
  rules = handler.getRules(stream, filename);
} catch (RuntimeException e) {
  if (e.getMessage().contains(MINPREVMATCHES)) {
    log.error("Remove duplicate minPrevMatches in rule XML: {}", e.getMessage());
  }
  throw e;
}

Prevention

When it happens

Trigger: Parsing pattern rule XML where <rulegroup minPrevMatches="2"> contains a <rule minPrevMatches="..."> — i.e. the attribute is specified at both levels.

Common situations: Merging rule XML files or copying rules between groups, moving minPrevMatches to the group but leaving it on child rules (or vice versa), and bulk edits that duplicate the attribute.

Related errors


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