stanfordnlp/CoreNLP · error
Got a close tag </ > which does not match any open tag
Error message
Got a close tag </${tag.name}> which does not match any open tag What it means
CleanXmlAnnotator strips XML/SGML-style markup from tokenized text while tracking an enclosing-tag stack. When it encounters a close tag </name> while the open-tag stack is empty, there is nothing to match it against; if allowFlawedXml is false it throws IllegalArgumentException, otherwise it logs this warning and skips the stray close tag.
Solutions
- Set the clean.xmlallowFlawedXml option (clean.xmlallowflawedxml=true) to tolerate unmatched tags, turning the exception into a logged warning.
- Fix the upstream text so fragments include their opening tags, or escape/remove stray '<'/'>' before tokenization.
- Validate/sanitize the XML markup before running the annotator (e.g., with an XML parser or regex repair pass).
- Wrap the annotation step in try/catch for IllegalArgumentException if the input is untrusted and skip failing documents.
Example fix
// before
props.setProperty("clean.xml", "true");
// throws on '</b>' with no opener
// after
props.setProperty("clean.xml", "true");
props.setProperty("clean.xmlallowflawedxml", "true"); // stray close tags are logged and skipped Defensive patterns
Strategy: try-catch
Validate before calling
// Quick sanity check for stray close tags before annotation
int open = 0;
for (String tok : tokens) {
if (tok.matches("<[^/][^>]*>")) open++;
if (tok.matches("</[^>]+>") && --open < 0) throw new IllegalArgumentException("Stray close tag: " + tok);
} Try / catch
// Tolerate malformed markup for untrusted input
try {
pipeline.annotate(doc);
} catch (IllegalArgumentException e) {
if (e.getMessage() != null && e.getMessage().contains("close tag")) {
// re-run with clean.xmlallowflawedxml=true or skip document
} else {
throw e;
}
} Prevention
- Set clean.xmlallowflawedxml=true when annotating document fragments or scraped text
- Repair or escape raw '<'/'>' characters in plain-text sources before tokenization
- Annotate complete documents rather than mid-tag excerpts
- Unit-test your preprocessing with representative malformed markup
When it happens
Trigger: Calling CleanXmlAnnotator.process on a CoreMap whose tokens contain an XML close tag with no preceding matching open tag — e.g., text fragment starting mid-document like '</p>' or an unescaped '<' token that the tokenizer split into a tag-like token.
Common situations: Annotating document fragments/excerpts cut from larger XML files; scraping pipeline output with broken markup; tokenizers treating stray '<' '>' characters as XML tokens in plain text.
Related errors
- Mismatched tags: </ > closed a < > tag.
- Unclosed tags, starting with <
- CORE: CoreLabel.initFromStrings: Can't handle
- Got a close tag </" + tag.name + "> which does not match…
- Expected left paren!
AI-assisted analysis of stanfordnlp/CoreNLP@1b7edd19c4 (2026-09-10).
Data as JSON: /api/errors/f0da001c367c54a2.
Report an issue: GitHub.
Appendix: source
Thrown at src/edu/stanford/nlp/pipeline/CleanXmlAnnotator.java:860
markSingleSentence = true;
}
}
if (xmlTagMatcher == null)
continue;
if (tag.isSingleTag) {
continue;
}
// at this point, we can't reuse the "currentTagSet" vector
// any more, since the current tag set has changed
currentTagSet = null;
if (tag.isEndTag) {
while (true) {
if (enclosingTags.isEmpty()) {
String mesg = "Got a close tag </" + tag.name + "> which does not match any open tag";
if (allowFlawedXml) {
log.warn(mesg);
break;
} else {
throw new IllegalArgumentException(mesg);
}
}
String lastTag = enclosingTags.pop();
if (xmlTagMatcher.matcher(lastTag).matches()) {
matchDepth--;
}
if (lastTag.equals(tag.name)) {
break;
}
String mesg = "Mismatched tags: </" + tag.name + "> closed a <" + lastTag + "> tag.";
if ( ! allowFlawedXml) {
throw new IllegalArgumentException(mesg);
} else {
log.warn(mesg);
}View on GitHub (pinned to 1b7edd19c4)