stanfordnlp/CoreNLP · error · IllegalArgumentException

Tree added a child before a label was added to a node! …

Error message

Tree added a child before a label was added to a node!  Offending proto: ${proto}

What it means

During fromProtoFlattenedTree reconstruction, an Open node marker may not be pushed onto the stack while the node on top of the stack still has no label — the flattened format requires label-before-children. Violating this ordering yields an IllegalArgumentException identifying the offending proto.

Solutions

  1. Build flattened trees with ProtobufAnnotationSerializer.toFlattenedTree so ordering is guaranteed.
  2. Reorder nodes so each Open node is immediately followed by its label node before any child Open.
  3. Catch IllegalArgumentException and log/replace the malformed parse annotation.

Example fix

// before (wrong order)
b.addNodes(open(parent)); b.addNodes(open(child)); b.addNodes(label(parent));
// after
b.addNodes(open(parent)); b.addNodes(label(parent)); b.addNodes(open(child));
Defensive patterns

Strategy: validation

Validate before calling

// every Open at depth>=1 must be preceded by a label for the parent
for (int i = 1; i < proto.getNodesCount(); i++) {
  var prev = proto.getNodes(i - 1);
  var cur = proto.getNodes(i);
  if (cur.hasOpenNode() && prev.hasOpenNode()) {
    throw new IllegalArgumentException("Open followed by Open without label at " + i);
  }
}

Try / catch

try {
  tree = ProtobufAnnotationSerializer.fromProto(proto);
} catch (IllegalArgumentException e) {
  log.warn("Bad node ordering in flattened tree: " + e.getMessage());
  tree = null;
}

Prevention

When it happens

Trigger: Deserializing a FlattenedParseTree proto where an OpenNode entry appears before a label/value entry for the currently open parent node, e.g. a hand-built proto with nodes ordered Open, Open, label instead of Open, label, Open.

Common situations: Custom code constructing FlattenedParseTree protos directly instead of using toFlattenedTree; language-specific writers that emit children before the node label.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/pipeline/ProtobufAnnotationSerializer.java:2216

    if (proto.getNodesList().size() == 0) {
      return null;
    }
    Stack<LabeledScoredTreeNode> stack = new Stack<>();
    LabeledScoredTreeNode finished = null;

    // The incoming data structure is basically a PTB formatted tree
    // with openNode representing ( and closeNode representing )
    // essentially we only need to keep track of the current node and
    // all of its ancestors
    // we do that in a stack.  as we finish a node, we add it to the
    // appropriate parent and forget about it
    for (CoreNLPProtos.FlattenedParseTree.Node next : proto.getNodesList()) {
      if (finished != null) {
        throw new IllegalArgumentException("Tree continued after it was already closed!  Offending proto: " + proto);
      }
      if (next.hasOpenNode()) {
        if (stack.size() > 0 && stack.peek().label() == null) {
          throw new IllegalArgumentException("Tree added a child before a label was added to a node!  Offending proto: " + proto);
        }
        LabeledScoredTreeNode newNode = new LabeledScoredTreeNode();
        stack.push(newNode);
        if (next.hasScore()) {
          newNode.setScore(next.getScore());
        }
      } else if (next.hasCloseNode()) {
        if (stack.size() == 0) {
          // demand that the tree always start with an Open
          throw new IllegalArgumentException("Tree started with a Close, not an Open!  Offending proto: " + proto);
        }
        LabeledScoredTreeNode child = stack.pop();
        if (stack.size() == 0) {
          // Popped off the last node.  Guess we're done.
          // We don't return yet so that we check that the
          // iterator is finished first
          finished = child;
        } else {

View on GitHub (pinned to 1b7edd19c4)