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
- 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)
- Verify with a tree traversal that every non-leaf, non-preterminal node has 2 children
- 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
- Collapse unary non-preterminal nodes before training
- Validate binarization of every tree in the dataset
- Use the same preprocessing pipeline for train and eval data
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
- SentimentCostAndGradient: Tree not correctly binarized:...
- We should not have reached leaves in forwardPropagate
- Not POS sequence for tree:
- Trees not of equal length
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)