languagetool-org/languagetool · error · IllegalStateException
Neither '<pattern>' tokens nor '<regexp>' is set in rule '
Error message
Neither '<pattern>' tokens nor '<regexp>' is set in rule '
What it means
A <rule> body must define either a <pattern> (token list) or a <regexp>. If neither was parsed by the time createRules builds the rule, it throws this IllegalStateException — the rule has no matchable content.
Source
Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java:799
rule.setXmlLineNumber(xmlLineNumber);
} else if (regex.length() > 0) {
// UNICODE_CHARACTER_CLASS (equivalent to the inline (?U) flag) makes \b, \w, \d and \s
// Unicode-aware. Without it the behavior of word boundaries next to non-ASCII letters
// depends on the JDK version (it changed between JDK 17 and JDK 21), which breaks many
// <regexp> rules. Setting it here avoids adding (?U) by hand to every <regexp> pattern.
int flags = Pattern.UNICODE_CHARACTER_CLASS | (regexCaseSensitive ? 0 : Pattern.CASE_INSENSITIVE|Pattern.UNICODE_CASE);
String regexStr = regex.toString();
if (regexMode == RegexpMode.SMART) {
// Note: it's not that easy to add \b because the regex might look like '(foo)' or '\d' so we cannot just look at the last character
regexStr = replaceSpacesInRegex(regexStr);
}
if (ruleAntiPatterns.size() > 0 || rulegroupAntiPatterns.size() > 0) {
throw new RuntimeException("<regexp> rules currently cannot be used together with <antipattern>. Rule id: " + id + "[" + subId + "]");
}
rule = new RegexPatternRule(id, name, message.toString(), shortMessage, suggestionsOutMsg.toString(), language, Pattern.compile(regexStr, flags), regexpMark);
rule.setSourceFile(sourceFile);
} else {
throw new IllegalStateException("Neither '<pattern>' tokens nor '<regexp>' is set in rule '" + id + "'");
}
setRuleFilter(filterClassName, filterArgs, rule);
prepareRule(rule);
rules.add(rule);
} else {
PatternToken patternToken = elemList.get(numElement);
if (patternToken.hasOrGroup()) {
// When creating a new rule, we finally clear the backed-up variables. All the elements in
// the OR group should share the values of backed-up variables. That's why these variables
// are backed-up.
List<Match> suggestionMatchesBackup = new ArrayList<>(suggestionMatches);
List<Match> suggestionMatchesOutMsgBackup = new ArrayList<>(suggestionMatchesOutMsg);
int startPosBackup = startPos;
int endPosBackup = endPos;
List<DisambiguationPatternRule> ruleAntiPatternsBackup = new ArrayList<>(ruleAntiPatterns);
for (PatternToken patternTokenOfOrGroup : patternToken.getOrGroup()) {
List<PatternToken> tmpElements2 = new ArrayList<>();
tmpElements2.addAll(tmpPatternTokens);View on GitHub (pinned to 2e990059ce)
Solutions
- Add a <pattern> element with at least one <token> to the rule
- Or add a <regexp> element with the regex to match
- Check the element spelling/casing — it must be exactly <pattern> or <regexp>
Example fix
// before <rule id="X" name="x"><message>...</message></rule> // after <rule id="X" name="x"><pattern><token>foo</token></pattern><message>...</message></rule>
Defensive patterns
Strategy: validation
Validate before calling
// Ensure each rule has a pattern or regexp body
NodeList rules = doc.getElementsByTagName("rule");
for (int i = 0; i < rules.getLength(); i++) {
Element r = (Element) rules.item(i);
if (r.getElementsByTagName("pattern").getLength() == 0 && r.getElementsByTagName("regexp").getLength() == 0)
throw new IllegalStateException("rule without pattern/regexp: " + r.getAttribute("id"));
} Try / catch
try { loader.parse(ruleXml, lang); } catch (IllegalStateException e) { if (e.getMessage().contains("Neither '<pattern>' tokens nor '<regexp>'")) { log.error("Add a <pattern> or <regexp> to the rule"); } throw e; } Prevention
- Never commit skeleton rules without a body
- Spell element names exactly: <pattern>, <regexp>
- Validate rule XML in unit tests so incomplete files fail fast
When it happens
Trigger: Parsing a rule XML where the rule element contains no <pattern> tokens and no <regexp> child; createRules is invoked from endElement when the rule closes.
Common situations: Skeleton/template rule XML left unfilled; XML where the pattern block was accidentally deleted or misnamed (e.g. <patterns>); typos in element names.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- id is null for rule with name '
- Cannot activate rule '
- '<marker>' may not be nested in rule '
- <antipattern>s can only contain <example>s without errors (i
- <regexp> rules currently cannot be used together with <antip
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/95ba8e16250aadaa.
Report an issue: GitHub.