stanfordnlp/CoreNLP · error · ForwardPropagationException

Non-preterminal nodes of size 1 should have already been…

Error message

Non-preterminal nodes of size 1 should have already been collapsed

What it means

The RNN model assumes binary-branching trees where any non-preterminal internal node has exactly two children. A non-preterminal node with a single child indicates the tree was not properly binarized/collapsed, so ForwardPropagationException is thrown.

Solutions

  1. Binarize and collapse unary (size-1) non-preterminal nodes before training (use the tooling in the sentiment package, e.g. CollinsHeadFinder-based binarization used in dataset conversion)
  2. Verify with a tree traversal that every non-leaf, non-preterminal node has 2 children
  3. Reconvert your dataset with ReadSentimentDataset, which performs the collapsing

Example fix

// before
(NP (NN dog))
// after (collapsed/binarized)
(NP* (NN dog))
Defensive patterns

Strategy: validation

Validate before calling

boolean isBinarized(Tree t) {
  if (t.isLeaf() || t.isPreTerminal()) return true;
  return t.children().length == 2 && Arrays.stream(t.children()).allMatch(this::isBinarized);
}
if (!isBinarized(root)) throw new IllegalStateException("Tree not binarized");

Try / catch

try { forwardPropagate(tree); } catch (ForwardPropagationException e) { if (e.getMessage().contains("size 1")) { collapseUnaries(tree); } else throw e; }

Prevention

When it happens

Trigger: Passing non-binarized constituency trees (unary productions like (NP (NN dog))) directly to training/evaluation without collapsing unary nodes.

Common situations: Using parser output without binarization, custom treebank preprocessing that skipped the collapse unary step, mixing trees from different preprocessing pipelines.

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/6bd1189bb6275ff8. Report an issue: GitHub.

Appendix: source

Thrown at src/edu/stanford/nlp/sentiment/SentimentCostAndGradient.java:509

   * useful annotation except when training.
   */
  public void forwardPropagateTree(Tree tree) {
    SimpleMatrix nodeVector; // initialized below or Exception thrown // = null;
    SimpleMatrix classification; // initialized below or Exception thrown // = null;

    if (tree.isLeaf()) {
      // We do nothing for the leaves.  The preterminals will
      // calculate the classification for this word/tag.  In fact, the
      // recursion should not have gotten here (unless there are
      // degenerate trees of just one leaf)
      throw new ForwardPropagationException("We should not have reached leaves in forwardPropagate");
    } else if (tree.isPreTerminal()) {
      classification = model.getUnaryClassification(tree.label().value());
      String word = tree.children()[0].label().value();
      SimpleMatrix wordVector = model.getWordVector(word);
      nodeVector = NeuralUtils.elementwiseApplyTanh(wordVector);
    } else if (tree.children().length == 1) {
      throw new ForwardPropagationException("Non-preterminal nodes of size 1 should have already been collapsed");
    } else if (tree.children().length == 2) {
      forwardPropagateTree(tree.children()[0]);
      forwardPropagateTree(tree.children()[1]);

      String leftCategory = tree.children()[0].label().value();
      String rightCategory = tree.children()[1].label().value();
      SimpleMatrix W = model.getBinaryTransform(leftCategory, rightCategory);
      classification = model.getBinaryClassification(leftCategory, rightCategory);

      SimpleMatrix leftVector = RNNCoreAnnotations.getNodeVector(tree.children()[0]);
      SimpleMatrix rightVector = RNNCoreAnnotations.getNodeVector(tree.children()[1]);
      SimpleMatrix childrenVector = NeuralUtils.concatenateWithBias(leftVector, rightVector);
      if (model.op.useTensors) {
        SimpleTensor tensor = model.getBinaryTensor(leftCategory, rightCategory);
        SimpleMatrix tensorIn = NeuralUtils.concatenate(leftVector, rightVector);
        SimpleMatrix tensorOut = tensor.bilinearProducts(tensorIn);
        nodeVector = NeuralUtils.elementwiseApplyTanh(W.mult(childrenVector).plus(tensorOut));
      } else {

View on GitHub (pinned to 1b7edd19c4)