languagetool-org/languagetool · error · IllegalArgumentException

RuleFilter: Index out of bounds in ${match.getRule().getFull

Error message

RuleFilter: Index out of bounds in ${match.getRule().getFullId()}, value: ${fromStr}

What it means

RuleFilter.getPosition() converts a position value like '1', '2marker' or 'rel:n' from the rule XML into a zero-based token index for the matched pattern. It throws IllegalArgumentException when the computed index is less than 1 or greater than the number of pattern tokens, i.e. the referenced position does not exist in the rule pattern.

Source

Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/RuleFilter.java:103

  }

  protected int getPosition(String fromStr, AnalyzedTokenReadings[] patternTokens, RuleMatch match) {
    int i;
    if (fromStr.startsWith("marker")) {
      i = 0;
      while (i < patternTokens.length && patternTokens[i].getStartPos() < match.getFromPos()
        || patternTokens[i].isSentenceStart()) {
        i++;
      }
      i++;
      if (fromStr.length()>6) {
        i += Integer.parseInt(fromStr.replace("marker", ""));
      }
    } else {
      i = Integer.parseInt(fromStr);
    }
    if (i < 1 || i > patternTokens.length) {
      throw new IllegalArgumentException("RuleFilter: Index out of bounds in "
        + match.getRule().getFullId() + ", value: " + fromStr);
    }
    return i - 1;
  }

  protected boolean isMatchAtSentenceStart(AnalyzedTokenReadings[] tokens, RuleMatch match) {
    int i = 0;
    while (i < tokens.length && tokens[i].getStartPos() < match.getFromPos()) {
      i++;
    }
    while (i > 0 && StringTools.isPunctuationMark(tokens[i].getToken())) {
      i--;
    }
    return i == 0;
  }

  // when there's a 'skip', we need to adapt the reference number
  protected int getSkipCorrectedReference(List<Integer> tokenPositions, int refNumber) {

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Count the tokens in the rule's <pattern> and set the position attribute to a value between 1 and that count
  2. If you recently changed the pattern, recompute all position attributes in the filter/tokenFilter elements
  3. Check marker/rel offset arithmetic in the filter source; ensure the referenced marker token exists
  4. Remember getPosition returns i-1; verify you are not double-adjusting an already 0-based value

Example fix

// before (XML)
<pattern>
  <token regexp="yes">be</token>
</pattern>
<filter class="MyFilter" args="position:3"/>
// after (XML)
<pattern>
  <token regexp="yes">be</token>
</pattern>
<filter class="MyFilter" args="position:1"/>
Defensive patterns

Strategy: validation

Validate before calling

int patternTokenCount = countPatternTokens(ruleXmlPattern);
int pos = Integer.parseInt(args.get("position").replace("marker", ""));
if (pos < 1 || pos > patternTokenCount) throw new IllegalStateException("position " + pos + " outside pattern of size " + patternTokenCount);

Type guard

boolean isValidPosition(String s, int n) { try { int i = Integer.parseInt(s); return i >= 1 && i <= n; } catch (NumberFormatException e) { return false; } }

Try / catch

try {
  int idx = getPosition(match, patternTokens, fromStr);
} catch (IllegalArgumentException e) {
  LOG.error("Filter position invalid for {}: {}", match.getRule().getFullId(), e.getMessage());
  return match; // or rethrow in strict mode
}

Prevention

When it happens

Trigger: An XML attribute such as tokenFilter pos='5' (or fromStr from marker/rel syntax) points beyond the number of tokens in the <pattern> of the rule, or evaluates to 0/negative after the 'marker'/rel offset arithmetic.

Common situations: Editing a rule and deleting or adding tokens to the pattern without updating the filter position; off-by-one confusion between the 1-based XML numbering and the returned 0-based index; using marker-based offsets in a rule without a <marker> element.

Related errors


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