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.

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.

Appendix: 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)