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
- Regenerate the FlattenedParseTree proto via toFlattenedTree instead of hand-building the nodes list.
- Verify the nodes list alternates Open/label/close correctly and ends at the root close.
- 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
- Only build FlattenedParseTree protos via toFlattenedTree
- Never append nodes after the root close marker
- Checksum or version-stamp serialized protos to detect corruption
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
- Tree added a child before a label was added to a node! …
- Tree started with a Close, not an Open! Offending proto
- Tree started with a label, not an Open! Offending proto
- Tree never finished! Offending proto
- Empty label not supported
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)