{"record":{"id":"141840f28ea6fa99","repo":"languagetool-org/languagetool","slug":"cannot-activate-rule","errorCode":null,"errorMessage":"Cannot activate rule '","messagePattern":"Cannot activate rule '","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java","lineNumber":905,"sourceCode":"    rule.setGoalSpecific(isGoalSpecific);\n    if (inRuleGroup) {\n      rule.setSubId(StringInterner.intern(Integer.toString(subId)));\n    } else {\n      rule.setSubId(\"1\");\n    }\n    caseSensitive = false;\n    for (Match m : suggestionMatches) {\n      rule.addSuggestionMatch(m);\n    }\n    if (phrasePatternTokens.size() <= 1) {\n      suggestionMatches.clear();\n    }\n    for (Match m : suggestionMatchesOutMsg) {\n      rule.addSuggestionMatchOutMsg(m);\n    }\n    suggestionMatchesOutMsg.clear();\n    if (category == null) {\n      throw new RuntimeException(\"Cannot activate rule '\" + id + \"', it is outside of a <category>...</category>\");\n    }\n    if (defaultOff) {\n      rule.setDefaultOff();\n    }\n    if (defaultTempOff) {\n      rule.setDefaultTempOff();\n    }\n    if (url != null && url.length() > 0) {\n      try {\n        String s = url.toString();\n        rule.setUrl(internUrl(s));\n      } catch (MalformedURLException e) {\n        throw new RuntimeException(\"Could not parse URL for rule: \" + rule + \": '\" + url + \"'\", e);\n      }\n    } else if (urlForRuleGroup != null && urlForRuleGroup.length() > 0) {\n      try {\n        rule.setUrl(internUrl(urlForRuleGroup.toString()));\n      } catch (MalformedURLException e) {","sourceCodeStart":887,"sourceCodeEnd":923,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java#L887-L923","documentation":"Every pattern rule must live inside a <category> element; the handler keeps the parsed category and throws this RuntimeException in prepareRule when it is null at rule creation time. Categories are how rules are grouped and toggled in the UI.","triggerScenarios":"Parsing a rule XML where <rule> (or <rulegroup>) appears at top level without an enclosing <category>...</category>.","commonSituations":"Extracting a single rule into a standalone test XML without copying its category; concatenating rule files and dropping the category wrapper.","solutions":["Wrap the rule (or rulegroup) in a <category> element with a name and id","Copy the category element from the original file the rule came from","For test XML fragments, add a minimal <category name=\"Test\" id=\"TEST\"> wrapper"],"exampleFix":"// before\n<rule id=\"X\" name=\"x\">...</rule>\n// after\n<category id=\"MYCAT\" name=\"My category\">\n  <rule id=\"X\" name=\"x\">...</rule>\n</category>","handlingStrategy":"validation","validationCode":"// Ensure every top-level rule/rulegroup is inside a category\nNodeList rules = doc.getElementsByTagName(\"rule\");\nfor (int i = 0; i < rules.getLength(); i++) {\n  Node p = rules.item(i).getParentNode();\n  boolean inCategory = false;\n  while (p != null) { if (\"category\".equals(p.getNodeName())) { inCategory = true; break; } p = p.getParentNode(); }\n  if (!inCategory) throw new IllegalStateException(\"rule outside <category>: \" + ((Element) rules.item(i)).getAttribute(\"id\"));\n}","typeGuard":null,"tryCatchPattern":"try { loader.parse(ruleXml, lang); } catch (RuntimeException e) { if (e.getMessage().contains(\"outside of a <category>\")) { log.error(\"Wrap rule in a <category> element\"); } throw e; }","preventionTips":["Always author rules inside <category> wrappers","When extracting rules for tests, copy the category too","Validate structure with the official rules DTD/schema"],"tags":["xml","category","rules","languagetool"],"backgroundTag":"schema-validation-failed","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"}