languagetool-org/languagetool · error · RuntimeException

Filter class '${className}' must implement interface ${RuleF

Error message

Filter class '${className}' must implement interface ${RuleFilter.class.getSimpleName()}

What it means

RuleFilterCreator.getFilter reflectively instantiates a filter class named in a rule's filter attribute and requires it to implement org.languagetool.rules.RuleFilter. If Class.forName succeeds and a no-arg constructor runs, but the resulting object is not an instance of RuleFilter, this RuntimeException is thrown. It signals that the class exists but does not satisfy the filter contract, so LanguageTool refuses to use it.

Source

Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/RuleFilterCreator.java:61

  public RuleFilter getFilter(String className) {
    try {
      Class<?> aClass = JLanguageTool.getClassBroker().forName(className);
      return myFilterCache.computeIfAbsent(aClass, clazz -> {
        Constructor<?>[] constructors = clazz.getConstructors();
        if (constructors.length != 1) {
          throw new RuntimeException("Constructor of filter class '"
            + className + "' must have exactly one constructor, but it has " + constructors.length);
        }
        Constructor<?> constructor = constructors[0];
        try {
          if (constructor.getParameterTypes().length != 0) {
            throw new RuntimeException("Constructor of filter class '" + className + "' must not have arguments: " + constructor);
          }
          Object filter = constructor.newInstance();
          if (filter instanceof RuleFilter) {
            return (RuleFilter) filter;
          } else {
            throw new RuntimeException("Filter class '" + className + "' must implement interface " + RuleFilter.class.getSimpleName());
          }
        } catch (Exception e) {
          throw new RuntimeException("Could not create filter class using constructor " + constructor, e);
        }
      });
    } catch (ClassNotFoundException e) {
      throw new RuntimeException("Could not find filter class: '"
              + className + "' - make sure to use a fully qualified class name like 'org.languagetool.rules.MyFilter'");
    }
  }

  public static @NotNull RuleFilterCreator getInstance() {
    return INSTANCE;
  }
}

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Make the referenced class implement org.languagetool.rules.RuleFilter (accept RuleMatch as first argument and return the correct match index from acceptRuleMatch).
  2. Check that the filter attribute in the rule XML contains the fully qualified name of the actual filter class, not another class with a similar name.
  3. Verify the class was not refactored: search the project for 'implements RuleFilter' and use one of those classes.
  4. Ensure the deployed JAR contains the updated class, not a stale build without the interface.

Example fix

// before
public class MyFilter {
  public int acceptRuleMatch(RuleMatch match, Map<String,String> args, int patternTokenPos, AnalyzedTokenReadings[] patternTokens) { ... }
}
// after
import org.languagetool.rules.RuleFilter;
public class MyFilter implements RuleFilter {
  @Override
  public RuleMatch acceptRuleMatch(RuleMatch match, Map<String,String> args, int patternTokenPos, AnalyzedTokenReadings[] patternTokens) { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> cls = Class.forName(fqcn);
if (!org.languagetool.rules.RuleFilter.class.isAssignableFrom(cls)) {
  throw new IllegalArgumentException(fqcn + " must implement RuleFilter");
}

Type guard

Object candidate = constructor.newInstance();
if (candidate instanceof RuleFilter) { /* safe cast */ }

Try / catch

try {
  RuleFilter f = RuleFilterCreator.getInstance().getFilter(fqcn);
} catch (RuntimeException e) {
  throw new IllegalStateException("Rule filter class invalid: " + fqcn, e);
}

Prevention

When it happens

Trigger: Calling RuleFilterCreator.getInstance().getFilter(className) where className resolves to a concrete class that does not implement RuleFilter. In rule XML files: a <filter class="..."> entry pointing at a class (e.g. a plain helper class or a Rule subclass) that lacks `implements RuleFilter`.

Common situations: Typing the wrong fully-qualified class name that happens to match another class; renaming/refactoring so the filter interface was dropped; pointing the filter attribute at a Rule or Test class instead of the filter; copying a rule snippet whose filter class was never implemented.

Understand the failure class

Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.

Related errors


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