languagetool-org/languagetool · error · IllegalArgumentException

maxOccurrences may not be 0

Error message

maxOccurrences may not be 0

What it means

PatternToken.setMaxOccurrence(int) rejects 0 outright: a token element must occur at least once or be declared optional via min occurrence, never 'zero times max'. Any call with i == 0 throws IllegalArgumentException before the range check.

Source

Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternToken.java:426

  /**
   * The minimum number of times this element may occur.
   * @param i currently only {@code 0} and {@code 1} are supported
   */
  public void setMinOccurrence(int i) {
    if (i != 0 && i != 1) {
      throw new IllegalArgumentException("minOccurrences must be 0 or 1: " + i);
    }
    setFlag(MAY_BE_OMITTED_MASK, i == 0);
  }

  /**
   * The maximum number of times this element may occur.
   * @param i a number >= 1 or {@code -1} for unlimited occurrences
   */
  public void setMaxOccurrence(int i) {
    if (i == 0) {
      throw new IllegalArgumentException("maxOccurrences may not be 0");
    }
    if (i < -1 || i > Byte.MAX_VALUE) {
      throw new IllegalArgumentException("maxOccurrences should be between -1 and " + Byte.MAX_VALUE + " but was: " + i);
    }
    maxOccurrence = (byte) i;
  }

  /**
   * Checks if the element has an exception for a previous token.
   * @return True if the element has a previous token matching exception.
   */
  public boolean hasPreviousException() {
    return rareFields != null && rareFields.previousExceptions.length > 0;
  }

  /**
   * Checks if the element has an exception for a next scope.
   * (only used for testing)

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Use setMinOccurrence(0) for an optional token instead of setMaxOccurrence(0).
  2. For truly optional repeats, call setMinOccurrence(0) plus setMaxOccurrence(n) (or -1 for unlimited).
  3. Fix rule XML: replace maxOccurs="0" with minOccurs="0" and a sensible max.
  4. Add a pre-call check: if (max == 0) treat as optional-token config instead.

Example fix

// before
pToken.setMaxOccurrence(0);
// after
pToken.setMinOccurrence(0);
pToken.setMaxOccurrence(-1); // optional, unlimited
Defensive patterns

Strategy: validation

Validate before calling

if (maxOccurrence == 0) {
  pToken.setMinOccurrence(0); // optional token instead
} else {
  pToken.setMaxOccurrence(maxOccurrence);
}

Type guard

boolean isValidMax(int i) { return i != 0; }

Try / catch

try {
  token.setMaxOccurrence(max);
} catch (IllegalArgumentException e) {
  token.setMinOccurrence(0); // interpret max=0 as optional
}

Prevention

When it happens

Trigger: Calling setMaxOccurrence(0), directly or via build() when rule XML specifies maxOccurs="0" or a builder passes 0 (see testZeroMinOccurrences2 / testZeroMinTwoMaxOccurrences).

Common situations: Authors who intend an optional token writing max="0" instead of min="0"; auto-generated rules from quantifier math where max computes to 0; confusion with regex {0} semantics.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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