stanfordnlp/CoreNLP · error · TsurgeonRuntimeException
Parents did not match for trees when applied to " + this
Error message
Parents did not match for trees when applied to " + this
What it means
CreateSubtreeNode.evaluate inserts the auxiliary tree around a range of child nodes; it first sanity-checks that the start and end matched nodes share the same parent. If the two child matchers resolved to nodes with different parents, the operation would splice an invalid structure, so it throws TsurgeonRuntimeException.
Solutions
- Rewrite the tregex pattern so both named nodes are siblings under a common parent (constrain with dominance/sibling relations).
- Restrict the match with additional relations (e.g. '$+' immediate-sibling or same-parent constraints) to guarantee a valid range.
- Catch TsurgeonRuntimeException around Tsurgeon.processQueries and log/skip non-conforming matches.
Example fix
// before String tregex = "NP <1 =start <2 =end"; // if start/end can bind under different parents // after String tregex = "@NP <1 =start <: =end"; // constrain both names within the same parent
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure both named nodes are under one parent before processing
boolean sameParent(TregexMatcher m, String n1, String n2, Tree root) {
Tree a = m.getNode(n1), b = m.getNode(n2);
return a != null && b != null && a.parent(root) == b.parent(root);
} Try / catch
try {
Tsurgeon.processPattern(pattern, ops, tree);
} catch (TsurgeonRuntimeException e) {
if (e.getMessage().contains("Parents did not match")) {
log.warn("Skipping tree: range nodes not siblings: " + e.getMessage());
} else throw e;
} Prevention
- Constrain named captures in tregex so range endpoints are siblings (use sibling/dominance relations).
- Test Tsurgeon scripts on a diverse sample of trees before batch processing.
- Skip-and-log non-conforming matches rather than letting one bad tree abort the corpus run.
When it happens
Trigger: A createSubtree operation with two child specifications (a range 'from...to') where the tregex match binds the two names to nodes under different parents — e.g. names matched at different levels of the tree.
Common situations: Tregex patterns that name nodes non-locally (e.g. '=start' matched deep and '=end' matched elsewhere) so a range operation spans different parents; patterns reused across trees where occasionally the two named nodes land in different subtrees.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Error -- no foot node found for " + originalTreeString
- Error -- two foot nodes in subtree" + t.toString()
- No foot node found for " + auxTree
- Node name " + name + " does not exist in the searched tree
- Attempted to replace a root node with more than one node…
AI-assisted analysis of stanfordnlp/CoreNLP@1b7edd19c4 (2026-09-10).
Data as JSON: /api/errors/8da00f4069978e52.
Report an issue: GitHub.
Appendix: source
Thrown at src/edu/stanford/nlp/trees/tregex/tsurgeon/CreateSubtreeNode.java:72
public Matcher(Map<String,Tree> newNodeNames, CoindexationGenerator coindexer) {
super(CreateSubtreeNode.this, newNodeNames, coindexer);
}
/**
* Combines all nodes between start and end into one subtree, then
* replaces those nodes with the new subtree in the corresponding
* location under parent
*/
@Override
public Tree evaluate(Tree tree, TregexMatcher tregex) {
Tree startChild = childMatcher[0].evaluate(tree, tregex);
Tree endChild = (childMatcher.length == 2) ? childMatcher[1].evaluate(tree, tregex) : startChild;
Tree parent = startChild.parent(tree);
// sanity check
if (parent != endChild.parent(tree)) {
throw new TsurgeonRuntimeException("Parents did not match for trees when applied to " + this);
}
AuxiliaryTree treeCopy = auxTree.copy(this, tree.treeFactory(), tree.label().labelFactory());
// Collect all the children of the parent of the node we care
// about. If the child is one of the nodes we care about, or
// between those two nodes, we add it to a list of inner children.
// When we reach the second endpoint, we turn that list of inner
// children into a new node using the newly created label. All
// other children are kept in an outer list, with the new node
// added at the appropriate location.
List<Tree> children = Generics.newArrayList();
List<Tree> innerChildren = Generics.newArrayList();
boolean insideSpan = false;
for (Tree child : parent.children()) {
if (child == startChild || child == endChild) {
if (!insideSpan && startChild != endChild) {
insideSpan = true;View on GitHub (pinned to 1b7edd19c4)