languagetool-org/languagetool · error · RuntimeException
Constructor of filter class '${className}' must not have arg
Error message
Constructor of filter class '${className}' must not have arguments: ${constructor} What it means
RuleFilterCreator.getFilter() reflectively instantiates the filter class and requires a public no-argument constructor. When the single public constructor takes parameters, it throws RuntimeException with the offending Constructor's toString, because the framework has no arguments to pass.
Source
Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/RuleFilterCreator.java:55
private RuleFilterCreator() {
}
/**
* @param className fully qualified class Name of a class implementing {@link RuleFilter}
*/
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() {View on GitHub (pinned to 2e990059ce)
Solutions
- Change the filter to a public no-argument constructor and move configuration into the args map (the key/value attributes passed to the filter at runtime)
- If state is needed, use mutable setters or static configuration initialized before rule loading
- Verify the class in <filter class='...'> is the intended filter, not a differently-constructed wrapper
Example fix
// before
public MyFilter(Language lang) { this.lang = lang; }
// after
public MyFilter() {}
@Override
public RuleMatch acceptRuleMatch(RuleMatch match, Map<String,String> args, int patternTokenPos, AnalyzedTokenReadings[] patternTokens) {
// read config from args instead of constructor
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> c = Class.forName(className);
for (java.lang.reflect.Constructor<?> ctor : c.getConstructors()) {
if (ctor.getParameterCount() != 0) throw new IllegalStateException(className + " must have a public no-arg constructor");
} Type guard
boolean hasNoArgPublicCtor(Class<?> c) { try { c.getConstructor(); return true; } catch (NoSuchMethodException e) { return false; } } Try / catch
try {
RuleFilter f = new RuleFilterCreator().getFilter(className);
} catch (RuntimeException e) {
if (e.getMessage().contains("must not have arguments")) {
throw new ConfigurationException("Filter " + className + " needs a no-arg constructor", e);
}
throw e;
} Prevention
- Never require constructor parameters in RuleFilter implementations; use the args map instead
- Verify with hasNoArgPublicCtor before registering a custom filter class
- Document filter configuration through the rule XML attributes, not the constructor
- Run rule-XML loading tests in CI to catch constructor issues before release
When it happens
Trigger: A filter class referenced by <filter class='...'> declares its only public constructor with one or more parameters (e.g. taking a Language or configuration object).
Common situations: Injecting dependencies via constructor in a filter and forgetting LanguageTool creates filters via a no-arg reflection call; migrating a filter class from another framework where constructor injection was allowed.
Related errors
- Constructor of filter class '${className}' must have exactly
- Format error in file ${path}, line: ${line}
- No IssueType found for name '" + name + "'. Valid values: "
- The number of forms and postags has to be the same in disamb
- Set 'no', 'regexp' and 'postag_regexp' for filter PartialPos
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/5966d60a0a4c2a68.
Report an issue: GitHub.