stanfordnlp/CoreNLP · error · RuntimeException

TIMEX3 should only contain text

Error message

TIMEX3 should only contain text ${child}

What it means

When converting GUTime's XML output into CoreMaps, toTimexCoreMaps expects every TIMEX3 element to wrap exactly one child node (the text). If a TIMEX3 element contains zero or more than one child node (e.g. nested markup or split text nodes), the annotator throws this RuntimeException because it cannot represent it as a single text timex.

Solutions

  1. Log the offending TIMEX3 element (it is included in the message) and inspect what extra child nodes it contains
  2. Strip/escape inline markup from the input text before annotation so GUTime emits plain-text TIMEX3 elements
  3. Preprocess the GUTime XML output to merge/split non-conforming TIMEX3 elements before calling toTimexCoreMaps
  4. Align the GUTime version with the one CoreNLP was built against

Example fix

// before
String text = docHtml; // contains <b>Feb 3</b> inline markup
guTimeAnnotator.annotate(new Annotation(text));
// after
String text = docHtml.replaceAll("<[^>]+>", ""); // plain text only
guTimeAnnotator.annotate(new Annotation(text));
Defensive patterns

Strategy: validation

Validate before calling

// Validate TIMEX3 elements in GUTime output before conversion
NodeList timexes = outputXML.getElementsByTagName("TIMEX3");
for (int i = 0; i < timexes.getLength(); i++) {
  Element e = (Element) timexes.item(i);
  if (e.getChildNodes().getLength() != 1)
    throw new IllegalStateException("Non-conforming TIMEX3 at index " + i + ": " + e.getTextContent());
}

Try / catch

try {
  guTimeAnnotator.annotate(annotation);
} catch (RuntimeException e) {
  if (e.getMessage() != null && e.getMessage().startsWith("TIMEX3 should only contain text")) {
    logger.warning("Skipping malformed TIMEX3: " + e.getMessage());
  } else throw e;
}

Prevention

When it happens

Trigger: GUTime emits TIMEX3 elements containing nested elements or multiple text nodes — typically due to unusual input markup, HTML fragments passed through to GUTime, or an unexpected GUTime version producing a different XML schema than expected.

Common situations: Annotating documents that already contain inline markup (XML/HTML) that survives into GUTime output; using a newer/older GUTime release whose TIMEX3 serialization differs from what the CoreNLP parser assumes.

Related errors


AI-assisted analysis of stanfordnlp/CoreNLP@1b7edd19c4 (2026-09-10). Data as JSON: /api/errors/408e3af7f0e44996. Report an issue: GitHub.

Appendix: source

Thrown at src/edu/stanford/nlp/time/GUTimeAnnotator.java:280

    for (int i = 0; i < docNodes.getLength(); i++) {
      Node n = docNodes.item(i);
      if ("text".equals(n.getNodeName())) {
        textElem = (Element) n;
        break;
      }
    }
    NodeList textNodes = textElem.getChildNodes();
    for (int i = 0; i < textNodes.getLength(); i++) {
      Node content = textNodes.item(i);
      if (content instanceof Text) {
        Text text = (Text)content;
        offset += text.getWholeText().length();
      } else if (content instanceof Element) {
        Element child = (Element)content;
        if (child.getNodeName().equals("TIMEX3")) {
          Timex timex = new Timex(child);
          if (child.getChildNodes().getLength() != 1) {
            throw new RuntimeException("TIMEX3 should only contain text " + child);
          }
          String timexText = child.getTextContent();
          CoreMap timexMap = new ArrayCoreMap();
          //(timex)
          timexMap.set(TimeAnnotations.TimexAnnotation.class, timex);
          //(text)
          timexMap.set(CoreAnnotations.TextAnnotation.class, timexText);
          //(characters)
          int charBegin = offset;
          timexMap.set(CoreAnnotations.CharacterOffsetBeginAnnotation.class, charBegin);
          offset += timexText.length();
          int charEnd = offset;
          timexMap.set(CoreAnnotations.CharacterOffsetEndAnnotation.class, charEnd);
          //(tokens)
          if(haveTokenOffsets){
            Integer tokBegin = beginMap.get(charBegin);
            int searchStep = 1;          //if no exact match, search around the character offset
            while(tokBegin == null){

View on GitHub (pinned to 1b7edd19c4)