{"record":{"id":"760ba72c9810a51f","repo":"stanfordnlp/CoreNLP","slug":"tree-continued-after-it-was-already-closed-offen","errorCode":null,"errorMessage":"Tree continued after it was already closed!  Offending proto: ${proto}","messagePattern":"Tree continued after it was already closed!  Offending proto: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/pipeline/ProtobufAnnotationSerializer.java","lineNumber":2212,"sourceCode":"  public static Tree fromProto(CoreNLPProtos.FlattenedParseTree proto) {\n    if (Thread.interrupted()) {\n      throw new RuntimeInterruptedException();\n    }\n    if (proto.getNodesList().size() == 0) {\n      return null;\n    }\n    Stack<LabeledScoredTreeNode> stack = new Stack<>();\n    LabeledScoredTreeNode finished = null;\n\n    // The incoming data structure is basically a PTB formatted tree\n    // with openNode representing ( and closeNode representing )\n    // essentially we only need to keep track of the current node and\n    // all of its ancestors\n    // we do that in a stack.  as we finish a node, we add it to the\n    // appropriate parent and forget about it\n    for (CoreNLPProtos.FlattenedParseTree.Node next : proto.getNodesList()) {\n      if (finished != null) {\n        throw new IllegalArgumentException(\"Tree continued after it was already closed!  Offending proto: \" + proto);\n      }\n      if (next.hasOpenNode()) {\n        if (stack.size() > 0 && stack.peek().label() == null) {\n          throw new IllegalArgumentException(\"Tree added a child before a label was added to a node!  Offending proto: \" + proto);\n        }\n        LabeledScoredTreeNode newNode = new LabeledScoredTreeNode();\n        stack.push(newNode);\n        if (next.hasScore()) {\n          newNode.setScore(next.getScore());\n        }\n      } else if (next.hasCloseNode()) {\n        if (stack.size() == 0) {\n          // demand that the tree always start with an Open\n          throw new IllegalArgumentException(\"Tree started with a Close, not an Open!  Offending proto: \" + proto);\n        }\n        LabeledScoredTreeNode child = stack.pop();\n        if (stack.size() == 0) {\n          // Popped off the last node.  Guess we're done.","sourceCodeStart":2194,"sourceCodeEnd":2230,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/pipeline/ProtobufAnnotationSerializer.java#L2194-L2230","documentation":"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.","triggerScenarios":"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.","commonSituations":"Protos produced by a different serializer version or manually assembled builders where closeNode markers were misplaced; truncated-then-padded messages from custom transport.","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."],"exampleFix":"// before\nCoreNLPProtos.FlattenedParseTree.Builder b = CoreNLPProtos.FlattenedParseTree.newBuilder();\nb.addNodes(open); b.addNodes(close); b.addNodes(extra); // extra node after root closed -> throws\n// after\nb.addNodes(open); b.addNodes(close); // exactly one complete tree","handlingStrategy":"try-catch","validationCode":"boolean complete = proto.getNodesCount() > 0;\nint depth = 0;\nfor (var n : proto.getNodesList()) {\n  if (n.hasOpenNode()) depth++;\n  if (n.hasCloseNode()) { depth--; if (depth == 0) complete = true; else if (depth < 0) complete = false; }\n  else if (complete) { complete = false; }\n}\nif (!complete || depth != 0) throw new IllegalArgumentException(\"malformed flattened tree proto\");","typeGuard":null,"tryCatchPattern":"try {\n  tree = ProtobufAnnotationSerializer.fromProto(proto);\n} catch (IllegalArgumentException e) {\n  log.warn(\"Corrupt flattened tree: \" + e.getMessage());\n  tree = null;\n}","preventionTips":["Only build FlattenedParseTree protos via toFlattenedTree","Never append nodes after the root close marker","Checksum or version-stamp serialized protos to detect corruption"],"tags":["serialization","protobuf","tree","malformed-input"],"backgroundTag":"protobuf-unmarshal-failed","analyzedSha":"1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a","analyzedAt":"2026-09-10T02:24:07.274Z","contentChangedAt":"2026-09-10T02:24:07.274Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}