{"record":{"id":"95ba8e16250aadaa","repo":"languagetool-org/languagetool","slug":"neither-pattern-tokens-nor-regexp-is-set-i","errorCode":null,"errorMessage":"Neither '<pattern>' tokens nor '<regexp>' is set in rule '","messagePattern":"Neither '<pattern>' tokens nor '<regexp>' is set in rule '","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java","lineNumber":799,"sourceCode":"        rule.setXmlLineNumber(xmlLineNumber);\n      } else if (regex.length() > 0) {\n        // UNICODE_CHARACTER_CLASS (equivalent to the inline (?U) flag) makes \\b, \\w, \\d and \\s\n        // Unicode-aware. Without it the behavior of word boundaries next to non-ASCII letters\n        // depends on the JDK version (it changed between JDK 17 and JDK 21), which breaks many\n        // <regexp> rules. Setting it here avoids adding (?U) by hand to every <regexp> pattern.\n        int flags = Pattern.UNICODE_CHARACTER_CLASS | (regexCaseSensitive ? 0 : Pattern.CASE_INSENSITIVE|Pattern.UNICODE_CASE);\n        String regexStr = regex.toString();\n        if (regexMode == RegexpMode.SMART) {\n          // 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\n          regexStr = replaceSpacesInRegex(regexStr);\n        }\n        if (ruleAntiPatterns.size() > 0 || rulegroupAntiPatterns.size() > 0) {\n          throw new RuntimeException(\"<regexp> rules currently cannot be used together with <antipattern>. Rule id: \" + id + \"[\" + subId + \"]\");\n        }\n        rule = new RegexPatternRule(id, name, message.toString(), shortMessage, suggestionsOutMsg.toString(), language, Pattern.compile(regexStr, flags), regexpMark);\n        rule.setSourceFile(sourceFile);\n      } else {\n        throw new IllegalStateException(\"Neither '<pattern>' tokens nor '<regexp>' is set in rule '\" + id + \"'\");\n      }\n      setRuleFilter(filterClassName, filterArgs, rule);\n      prepareRule(rule);\n      rules.add(rule);\n    } else {\n      PatternToken patternToken = elemList.get(numElement);\n      if (patternToken.hasOrGroup()) {\n        // When creating a new rule, we finally clear the backed-up variables. All the elements in\n        // the OR group should share the values of backed-up variables. That's why these variables\n        // are backed-up.\n        List<Match> suggestionMatchesBackup = new ArrayList<>(suggestionMatches);\n        List<Match> suggestionMatchesOutMsgBackup =  new ArrayList<>(suggestionMatchesOutMsg);\n        int startPosBackup = startPos;\n        int endPosBackup = endPos;\n        List<DisambiguationPatternRule> ruleAntiPatternsBackup = new ArrayList<>(ruleAntiPatterns);\n        for (PatternToken patternTokenOfOrGroup : patternToken.getOrGroup()) {\n          List<PatternToken> tmpElements2 = new ArrayList<>();\n          tmpElements2.addAll(tmpPatternTokens);","sourceCodeStart":781,"sourceCodeEnd":817,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java#L781-L817","documentation":"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.","triggerScenarios":"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.","commonSituations":"Skeleton/template rule XML left unfilled; XML where the pattern block was accidentally deleted or misnamed (e.g. <patterns>); typos in element names.","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>"],"exampleFix":"// before\n<rule id=\"X\" name=\"x\"><message>...</message></rule>\n// after\n<rule id=\"X\" name=\"x\"><pattern><token>foo</token></pattern><message>...</message></rule>","handlingStrategy":"validation","validationCode":"// Ensure each rule has a pattern or regexp body\nNodeList rules = doc.getElementsByTagName(\"rule\");\nfor (int i = 0; i < rules.getLength(); i++) {\n  Element r = (Element) rules.item(i);\n  if (r.getElementsByTagName(\"pattern\").getLength() == 0 && r.getElementsByTagName(\"regexp\").getLength() == 0)\n    throw new IllegalStateException(\"rule without pattern/regexp: \" + r.getAttribute(\"id\"));\n}","typeGuard":null,"tryCatchPattern":"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; }","preventionTips":["Never commit skeleton rules without a body","Spell element names exactly: <pattern>, <regexp>","Validate rule XML in unit tests so incomplete files fail fast"],"tags":["xml","rules","missing-content","languagetool"],"backgroundTag":"missing-required-argument","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"}