{"record":{"id":"e0cabc23a33864eb","repo":"languagetool-org/languagetool","slug":"filter-class-classname-must-implement-interfa-e0cabc","errorCode":null,"errorMessage":"Filter class '${className}' must implement interface ${RuleFilter.class.getSimpleName()}","messagePattern":"Filter class '(.+?)' must implement interface (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"languagetool-core/src/main/java/org/languagetool/rules/patterns/RuleFilterCreator.java","lineNumber":61,"sourceCode":"  public RuleFilter getFilter(String className) {\n    try {\n      Class<?> aClass = JLanguageTool.getClassBroker().forName(className);\n      return myFilterCache.computeIfAbsent(aClass, clazz -> {\n        Constructor<?>[] constructors = clazz.getConstructors();\n        if (constructors.length != 1) {\n          throw new RuntimeException(\"Constructor of filter class '\"\n            + className + \"' must have exactly one constructor, but it has \" + constructors.length);\n        }\n        Constructor<?> constructor = constructors[0];\n        try {\n          if (constructor.getParameterTypes().length != 0) {\n            throw new RuntimeException(\"Constructor of filter class '\" + className + \"' must not have arguments: \" + constructor);\n          }\n          Object filter = constructor.newInstance();\n          if (filter instanceof RuleFilter) {\n            return (RuleFilter) filter;\n          } else {\n            throw new RuntimeException(\"Filter class '\" + className + \"' must implement interface \" + RuleFilter.class.getSimpleName());\n          }\n        } catch (Exception e) {\n          throw new RuntimeException(\"Could not create filter class using constructor \" + constructor, e);\n        }\n      });\n    } catch (ClassNotFoundException e) {\n      throw new RuntimeException(\"Could not find filter class: '\"\n              + className + \"' - make sure to use a fully qualified class name like 'org.languagetool.rules.MyFilter'\");\n    }\n  }\n\n  public static @NotNull RuleFilterCreator getInstance() {\n    return INSTANCE;\n  }\n}\n","sourceCodeStart":43,"sourceCodeEnd":77,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-core/src/main/java/org/languagetool/rules/patterns/RuleFilterCreator.java#L43-L77","documentation":"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.","triggerScenarios":"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`.","commonSituations":"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.","solutions":["Make the referenced class implement org.languagetool.rules.RuleFilter (accept RuleMatch as first argument and return the correct match index from acceptRuleMatch).","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.","Verify the class was not refactored: search the project for 'implements RuleFilter' and use one of those classes.","Ensure the deployed JAR contains the updated class, not a stale build without the interface."],"exampleFix":"// before\npublic class MyFilter {\n  public int acceptRuleMatch(RuleMatch match, Map<String,String> args, int patternTokenPos, AnalyzedTokenReadings[] patternTokens) { ... }\n}\n// after\nimport org.languagetool.rules.RuleFilter;\npublic class MyFilter implements RuleFilter {\n  @Override\n  public RuleMatch acceptRuleMatch(RuleMatch match, Map<String,String> args, int patternTokenPos, AnalyzedTokenReadings[] patternTokens) { ... }\n}","handlingStrategy":"validation","validationCode":"Class<?> cls = Class.forName(fqcn);\nif (!org.languagetool.rules.RuleFilter.class.isAssignableFrom(cls)) {\n  throw new IllegalArgumentException(fqcn + \" must implement RuleFilter\");\n}","typeGuard":"Object candidate = constructor.newInstance();\nif (candidate instanceof RuleFilter) { /* safe cast */ }","tryCatchPattern":"try {\n  RuleFilter f = RuleFilterCreator.getInstance().getFilter(fqcn);\n} catch (RuntimeException e) {\n  throw new IllegalStateException(\"Rule filter class invalid: \" + fqcn, e);\n}","preventionTips":["Always declare filter classes as `implements RuleFilter`.","Keep filter classes in a dedicated package so wrong classes are not referenced by name.","Add a unit test that reflectively loads every class referenced by filter attributes and asserts RuleFilter assignability."],"tags":["java","reflection","language-tool","rule-filters"],"backgroundTag":"incompatible-source-type","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}