languagetool-org/languagetool · error · IllegalStateException

'<marker>' may not be nested in rule '

Error message

'<marker>' may not be nested in rule '

What it means

LanguageTool's <marker> element marks the error span inside a pattern or example and must not be nested inside another <marker>. The SAX handler tracks inMarker and throws IllegalStateException on a second opening marker while already inside one.

Source

Thrown at languagetool-core/src/main/java/org/languagetool/rules/patterns/PatternRuleHandler.java:449

        if (minPrevMatchesStr2 != null) {
          ruleGroupMinPrevMatches = Integer.parseInt(minPrevMatchesStr2);  
        }
        String distanceTokensStr2 = attrs.getValue(DISTANCETOKENS);
        if (distanceTokensStr2 != null) {
          ruleGroupDistanceTokens = Integer.parseInt(distanceTokensStr2);  
        }
        antipatternForRuleGroupsExamples = new ArrayList<>();
        String prioRuleGroupAttributeValue = attrs.getValue(PRIO);
        if (prioRuleGroupAttributeValue != null) {
          prioRuleGroupAttribute = Integer.parseInt(prioRuleGroupAttributeValue);
        }
        break;
      case MATCH:
        setMatchElement(attrs, inSuggestion && (isSuggestionSuppressMisspelled || isRuleSuppressMisspelled));
        break;
      case MARKER:
        if (inMarker) {
          throw new IllegalStateException("'<marker>' may not be nested in rule '" + id + "'");
        }
        if (inIncorrectExample) {
          incorrectExample.append(MARKER_TAG);
        } else if (inCorrectExample) {
          correctExample.append(MARKER_TAG);
        } else if (inErrorTriggerExample) {
          errorTriggerExample.append(MARKER_TAG);
        } else if (inPattern || inAntiPattern) {
          startPos = tokenCounter;
          inMarker = true;
        }
        break;
      case UNIFICATION:
        uFeature = attrs.getValue("feature");
        inUnificationDef = true;
        break;
      case "equivalence":
        uType = attrs.getValue(TYPE);

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Remove the outer or inner duplicate <marker> so only one nesting level remains
  2. Split the marked region so each span is marked once, side by side, not nested
  3. If marking two separate tokens, use two adjacent markers at the same level (check rule semantics) or a single wider marker

Example fix

// before
<pattern><marker><token>a</token><marker><token>b</token></marker></marker></pattern>
// after
<pattern><marker><token>a</token><token>b</token></marker></pattern>
Defensive patterns

Strategy: validation

Validate before calling

// Reject nested markers before loading
NodeList markers = doc.getElementsByTagName("marker");
for (int i = 0; i < markers.getLength(); i++) {
  if (markers.item(i).getChildNodes().getLength() > 0) {
    NodeList kids = markers.item(i).getChildNodes();
    for (int j = 0; j < kids.getLength(); j++)
      if ("marker".equals(kids.item(j).getNodeName()))
        throw new IllegalStateException("nested <marker>");
  }
}

Try / catch

try { loader.parse(ruleXml, lang); } catch (IllegalStateException e) { if (e.getMessage().contains("may not be nested")) { log.error("Un-nest <marker> elements"); } throw e; }

Prevention

When it happens

Trigger: XML containing <marker>...<marker>...</marker>...</marker> within a rule pattern, suggestion, or example. Raised in startElement case MARKER when inMarker is already true.

Common situations: Copy-paste merging two marked spans; careless editing that wraps an existing marker in another marker; automated XML generation that emits nested markers.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/2a946fea43c026af. Report an issue: GitHub.