stanfordnlp/CoreNLP · error · IllegalArgumentException

Tree continued after it was already closed! Offending proto

Error message

Tree continued after it was already closed!  Offending proto: ${proto}

What it means

fromProtoFlattenedTree rebuilds a Tree from a FlattenedParseTree proto using a stack, and sets a 'finished' sentinel once the root node is popped. If more nodes follow after the tree is complete, the proto is malformed, so an IllegalArgumentException naming the offending proto is thrown.

Solutions

  1. Regenerate the FlattenedParseTree proto via toFlattenedTree instead of hand-building the nodes list.
  2. Verify the nodes list alternates Open/label/close correctly and ends at the root close.
  3. Catch IllegalArgumentException and treat the annotation as missing, re-running the parser if needed.

Example fix

// before
CoreNLPProtos.FlattenedParseTree.Builder b = CoreNLPProtos.FlattenedParseTree.newBuilder();
b.addNodes(open); b.addNodes(close); b.addNodes(extra); // extra node after root closed -> throws
// after
b.addNodes(open); b.addNodes(close); // exactly one complete tree
Defensive patterns

Strategy: try-catch

Validate before calling

boolean complete = proto.getNodesCount() > 0;
int depth = 0;
for (var n : proto.getNodesList()) {
  if (n.hasOpenNode()) depth++;
  if (n.hasCloseNode()) { depth--; if (depth == 0) complete = true; else if (depth < 0) complete = false; }
  else if (complete) { complete = false; }
}
if (!complete || depth != 0) throw new IllegalArgumentException("malformed flattened tree proto");

Try / catch

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

Prevention

When it happens

Trigger: Deserializing a FlattenedParseTree proto whose nodes list contains extra entries after the final CloseNode that completed the root — e.g. a corrupted, hand-edited, or wrongly concatenated flattened tree.

Common situations: Protos produced by a different serializer version or manually assembled builders where closeNode markers were misplaced; truncated-then-padded messages from custom transport.

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/760ba72c9810a51f. Report an issue: GitHub.

Appendix: source

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

  public static Tree fromProto(CoreNLPProtos.FlattenedParseTree proto) {
    if (Thread.interrupted()) {
      throw new RuntimeInterruptedException();
    }
    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.

View on GitHub (pinned to 1b7edd19c4)