stanfordnlp/CoreNLP · error · ParseException

Illegal number of nodes given to createSubtree (

Error message

Illegal number of nodes given to createSubtree (

What it means

Same error as the generated parser at TsurgeonParser.jj:303, but this is the source grammar location (TsurgeonParser.jjt:177): createSubtree only supports one or two node selections. The parser builds a CreateSubtreeNode from the collected selections and throws a ParseException for any other count.

Solutions

  1. Restrict createSubtree usage to 1 or 2 node selections in the tsurgeon script.
  2. If you need to group more than two nodes, chain multiple createSubtree/adjoin operations.
  3. Update downstream generators to assert list size ∈ {1,2} before emitting the operation.

Example fix

// before
createSubtree VP n1 n2 n3
// after
createSubtree VP n1 n2
Defensive patterns

Strategy: validation

Validate before calling

boolean opArityOk(String op) {
  String[] p = op.trim().split("\\s+");
  return !p[0].equals("createSubtree") || (p.length == 3 || p.length == 4);
}

Try / catch

try { return Tsurgeon.parseOperation(script); } catch (ParseException e) { log.error("Bad createSubtree arity: " + script); return null; }

Prevention

When it happens

Trigger: A Tsurgeon operation "createSubtree <label> <nodes>" where the NodeSelectionList resolves to 0, 3, or more selections when the operation block executes.

Common situations: Editing .jjt grammar or tsurgeon scripts and changing the selection count; writing operations generically from a template that emits a variable-length node list.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/trees/tregex/tsurgeon/TsurgeonParser.jjt:177

  operator = <REPLACE> child1 = NodeSelection() treeList = TreeList(false)
    { return new ReplaceNode(child1,treeList); }
| operator = <MOVE> child1 = NodeSelection() loc = Location()
    { return new MoveNode(child1, loc, false); }
| operator = <MOVE_PRUNE> child1 = NodeSelection() loc = Location()
    { return new MoveNode(child1, loc, true); }
| LOOKAHEAD(3)
  operator = <INSERT> child1 = NodeSelection() loc = Location()
    { return new InsertNode(child1, loc); }
| LOOKAHEAD(3)
  operator = <INSERT> tree = TreeRoot(false) loc = Location()
    { return new InsertNode(tree, loc); }
| operator = <CREATE_SUBTREE> tree = TreeRoot(false) nodeSelections = NodeSelectionList(new ArrayList<TsurgeonPattern>())
    { if (nodeSelections.size() == 1) {
        return new CreateSubtreeNode(nodeSelections.get(0), tree);
      } else if (nodeSelections.size() == 2) {
        return new CreateSubtreeNode(nodeSelections.get(0), nodeSelections.get(1), tree);
      } else {
        throw new ParseException("Illegal number of nodes given to createSubtree (" + nodeSelections.size() + ")");
      }
    }
| operator = <ADJOIN> tree = TreeRoot(true) child1 = NodeSelection()
    { return new AdjoinNode(tree, child1); }
| operator = <ADJOIN_TO_HEAD> tree = TreeRoot(true) child1 = NodeSelection()
    { return new AdjoinToHeadNode(tree, child1); }
| operator = <ADJOIN_TO_FOOT> tree = TreeRoot(true) child1 = NodeSelection()
    { return new AdjoinToFootNode(tree, child1); }
| operator = <COINDEX> nodeSelections = NodeSelectionList(new ArrayList<TsurgeonPattern>())
    { return new CoindexNodes(nodeSelections.toArray(new TsurgeonPattern[] {})); }
}

TreeLocation Location() :
{
  Token rel;
  TsurgeonPattern child;
}
{

View on GitHub (pinned to 1b7edd19c4)