stanfordnlp/CoreNLP · error · TsurgeonRuntimeException

Attempted to replace a root node with more than one node…

Error message

Attempted to replace a root node with more than one node, unable to proceed

What it means

ReplaceNode.evaluate replaces the matched node with new children. When the matched node is the root of the whole tree, the replacement must yield exactly one root; if more than one replacement node was supplied, the operation cannot attach multiple roots and throws TsurgeonRuntimeException.

Solutions

  1. Make the tregex pattern match only non-root nodes (e.g. require a parent: "@NP < __" or exclude ROOT).
  2. Supply exactly one replacement node if the pattern may match the root.
  3. Catch TsurgeonRuntimeException and skip/adjust the match when replacing roots with multiple nodes is intended to be disallowed.

Example fix

// before
tregex: "@ROOT" ; replace: =root /S/ /NP/  // multi-node replace on root
// after
tregex: "@ROOT <1=child" ; replace: =child /S/  // replace a non-root child, single node
Defensive patterns

Strategy: try-catch

Validate before calling

// only allow multi-node replacements for non-root matches
boolean safeReplace(String tregex, int replacementCount) {
  boolean matchesRoot = tregex.contains("ROOT") || !tregex.contains("<");
  return !(matchesRoot && replacementCount > 1);
}

Try / catch

try {
  tree = op.evaluate(tree, matcher);
} catch (TsurgeonRuntimeException e) {
  if (e.getMessage().contains("replace a root node")) {
    log.warn("Skipping root replacement: " + e.getMessage());
  } else throw e;
}

Prevention

When it happens

Trigger: A replace Tsurgeon operation (replace <match> <node1> <node2> ...) where the tregex match binds the root node (oldNode == tree) and the replacement list contains more than two entries (children.length > 2, i.e. more than one replacement node).

Common situations: Writing 'replace =root X Y' style Tsurgeon scripts; patterns that unintentionally match the root (e.g. bare tree-level labels like "ROOT" or "@S" matching the top node); reusing a multi-node replacement script against patterns that also hit roots.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/trees/tregex/tsurgeon/ReplaceNode.java:41

  private static final Function<AuxiliaryTree, HoldTreeNode> convertAuxiliaryToHold = t -> new HoldTreeNode(t);

  @Override
  public TsurgeonMatcher matcher(Map<String,Tree> newNodeNames, CoindexationGenerator coindexer) {
    return new Matcher(newNodeNames, coindexer);
  }

  private class Matcher extends TsurgeonMatcher {
    public Matcher(Map<String,Tree> newNodeNames, CoindexationGenerator coindexer) {
      super(ReplaceNode.this, newNodeNames, coindexer);
    }

    @Override
    public Tree evaluate(Tree tree, TregexMatcher tregex) {
      Tree oldNode = childMatcher[0].evaluate(tree, tregex);
      if (oldNode==tree) {
        if (children.length > 2) {
          throw new TsurgeonRuntimeException("Attempted to replace a root node with more than one node, unable to proceed");
        }
        return childMatcher[1].evaluate(tree, tregex);
      }
      Tree parent = oldNode.parent(tree);
      int i = parent.objectIndexOf(oldNode);
      parent.removeChild(i);
      for (int j = 1; j < children.length; ++j) {
        Tree newNode = childMatcher[j].evaluate(tree, tregex);
        parent.insertDtr(newNode.deepCopy(), i + j - 1);
      }
      return tree;
    }
  }
}

View on GitHub (pinned to 1b7edd19c4)