stanfordnlp/CoreNLP · error · RuntimeException

Found two roots for sentence " + sentence

Error message

Found two roots for sentence " + sentence

What it means

When converting the raw dataset format into Trees, ReadSentimentDataset expects exactly one node with parent pointer -1 (the root) per sentence. If the parent-pointer list contains two or more -1 entries, the converter cannot determine a single root and throws RuntimeException.

Solutions

  1. Inspect the sentence's parent-pointer lines and ensure exactly one has -1
  2. Re-extract or re-download the pristine Stanford Sentiment Treebank dataset
  3. Fix your preprocessing script so only the top node has parent -1

Example fix

// before (data)
0
-1
-1
// after (data)
0
-1
1
Defensive patterns

Strategy: validation

Validate before calling

long roots = parentPointers.stream().filter(p -> p == -1).count();
if (roots != 1) throw new IllegalArgumentException("Expected exactly 1 root, found " + roots);

Try / catch

try { converter.main(args); } catch (RuntimeException e) { if (e.getMessage().contains("Found two roots")) { reportCorruptSentence(e); } else throw e; }

Prevention

When it happens

Trigger: Parsing a corrupted or hand-edited sentence file in the sentiment dataset format where more than one line has parent index -1, or a bug in custom dataset generation producing disconnected components.

Common situations: Manually modified train.txt/dev.txt files, partial downloads that merged chunks of two sentences, or custom-preprocessed data with wrong parent indexing.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/sentiment/ReadSentimentDataset.java:242

    Tree[] subtrees = new Tree[maxNode + 1];
    for (int i = 0; i < sentence.size(); ++i) {
      CoreLabel word = new CoreLabel();
      word.setValue(sentence.get(i));
      Tree leaf = new LabeledScoredTreeNode(word);
      subtrees[i] = new LabeledScoredTreeNode(new CoreLabel());
      subtrees[i].addChild(leaf);
    }

    for (int i = sentence.size(); i <= maxNode; ++i) {
      subtrees[i] = new LabeledScoredTreeNode(new CoreLabel());
    }

    boolean[] connected = new boolean[maxNode + 1];
    Tree root = null;
    for (int index = 0; index < parentPointers.size(); ++index) {
      if (parentPointers.get(index) == -1) {
        if (root != null) {
          throw new RuntimeException("Found two roots for sentence " + sentence);
        }
        root = subtrees[index];
      } else {
        // Walk up the tree structure to make sure that leftmost
        // phrases are added first.  Otherwise, if the numbers are
        // inverted, we might get the right phrase added to a parent
        // first, resulting in "case zero in this", for example,
        // instead of "in this case zero"
        // Note that because we keep track of which ones are already
        // connected, we process this at most once per parent, so the
        // overall construction time is still efficient.
        connect(parentPointers, subtrees, connected, index);
      }
    }

    for (int i = 0; i <= maxNode; ++i) {
      List<Tree> leaves = subtrees[i].getLeaves();
      List<String> words = CollectionUtils.transformAsList(leaves, TRANSFORM_TREE_TO_WORD);

View on GitHub (pinned to 1b7edd19c4)