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

  1. Rewrite the tregex pattern so both named nodes are siblings under a common parent (constrain with dominance/sibling relations).
  2. Restrict the match with additional relations (e.g. '$+' immediate-sibling or same-parent constraints) to guarantee a valid range.
  3. 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

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


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)