{"record":{"id":"94a10acd36318014","repo":"stanfordnlp/CoreNLP","slug":"cannot-make-a-mergenodes-out-of-fewer-than-2-nodes","errorCode":null,"errorMessage":"Cannot make a MergeNodes out of fewer than 2 nodes (got \" + argsBox.nodes.size() + \")","messagePattern":"Cannot make a MergeNodes out of fewer than 2 nodes \\(got \" \\+ argsBox\\.nodes\\.size\\(\\) \\+ \"\\)","errorType":"exception","errorClass":"SsurgeonParseException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/semgraph/semgrex/ssurgeon/Ssurgeon.java","lineNumber":651,"sourceCode":"        return new DeleteGraphFromNode(argsBox.nodes.get(0));\n      } else if (command.equalsIgnoreCase(DeleteLeaf.LABEL)) {\n        if (argsBox.nodes.size() != 1) {\n          throw new SsurgeonParseException(\"Cannot make a DeleteLeaf out of \" + argsBox.nodes.size() + \" nodes\");\n        }\n        return new DeleteLeaf(argsBox.nodes.get(0));\n      } else if (command.equalsIgnoreCase(EditNode.LABEL)) {\n        if (argsBox.nodes.size() != 1) {\n          throw new SsurgeonParseException(\"Cannot make an EditNode out of \" + argsBox.nodes.size() + \" nodes.  Please use exactly one -node\");\n        }\n        return new EditNode(argsBox.nodes.get(0), argsBox.annotations, argsBox.updateMorphoFeatures, argsBox.remove, argsBox.removeMorphoFeatures);\n      } else if (command.equalsIgnoreCase(Lemmatize.LABEL)) {\n        if (argsBox.nodes.size() != 1) {\n          throw new SsurgeonParseException(\"Cannot make a Lemmatize out of \" + argsBox.nodes.size() + \" nodes.  Please use exactly one -node\");\n        }\n        return new Lemmatize(argsBox.nodes.get(0), language);\n      } else if (command.equalsIgnoreCase(MergeNodes.LABEL)) {\n        if (argsBox.nodes.size() < 2) {\n          throw new SsurgeonParseException(\"Cannot make a MergeNodes out of fewer than 2 nodes (got \" + argsBox.nodes.size() + \")\");\n        }\n        return new MergeNodes(argsBox.nodes, argsBox.annotations);\n      } else if (command.equalsIgnoreCase(RelabelNamedEdge.LABEL)) {\n        if (argsBox.reln == null) {\n          throw new SsurgeonParseException(\"Relation not specified for AddEdge\");\n        }\n        GrammaticalRelation reln = GrammaticalRelation.valueOf(language, argsBox.reln);\n        return new RelabelNamedEdge(argsBox.edge, reln);\n      } else if (command.equalsIgnoreCase(RemoveEdge.LABEL)) {\n        GrammaticalRelation reln = null;\n        if (argsBox.reln != null) {\n          reln = GrammaticalRelation.valueOf(argsBox.reln);\n        }\n        return new RemoveEdge(reln, argsBox.govNodeName, argsBox.dep);\n      } else if (command.equalsIgnoreCase(RemoveNamedEdge.LABEL)) {\n        return new RemoveNamedEdge(argsBox.edge);\n      } else if (command.equalsIgnoreCase(KillAllIncomingEdges.LABEL)) {\n        if (argsBox.nodes.size() != 1) {","sourceCodeStart":633,"sourceCodeEnd":669,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/semgraph/semgrex/ssurgeon/Ssurgeon.java#L633-L669","documentation":"Ssurgeon's parseEditLine builds a MergeNodes edit from a semgrex ssurgeon resource line. A MergeNodes operation merges two or more matched nodes into one, so the parser requires at least 2 -node entries on the edit line. If fewer are given, it throws SsurgeonParseException before constructing the edit.","triggerScenarios":"An ssurgeon edit line like 'merge-grams -node:foo' with only one -node argument (or none) is parsed via Ssurgeon.parseEditLine; argsBox.nodes.size() < 2 throws.","commonSituations":"Hand-written ssurgeon rule files where the author forgot the second -node, or a rule generator emitted a merge with only one node (e.g. after filtering removed a node name).","solutions":["Add at least two -node:NAME arguments naming nodes matched by the semgrex query","Verify the node names exist in the preceding semgrex pattern so they are captured in argsBox","If you truly operate on one node, use a different edit (e.g. relabel-node, lemmatize) instead of merge-grams"],"exampleFix":"# before\nssurgeon: { dog } :: merge-grams -node:dog\n# after\nssurgeon: { dog & dep(gov,dog) } :: merge-grams -node:dog -node:gov","handlingStrategy":"validation","validationCode":"// before parsing, verify the merge line has >= 2 -node args\nboolean validMerge(String line) {\n  return line.startsWith(\"merge-grams\") &&\n         line.split(\"-node:\", -1).length - 1 >= 2;\n}","typeGuard":null,"tryCatchPattern":"try {\n  SsurgeonEdit e = Ssurgeon.parseEditLine(line);\n} catch (SsurgeonParseException ex) {\n  log.severe(\"Bad merge-grams rule: \" + line + \" -> \" + ex.getMessage());\n}","preventionTips":["Count -node: occurrences in each merge-grams line before shipping rule files","Validate ssurgeon rule files in CI by parsing them with parseEditLine","Use a different single-node edit when the rule only touches one node"],"tags":["java","ssurgeon","parser","argument-count"],"backgroundTag":"invalid-argument-value","analyzedSha":"1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a","analyzedAt":"2026-09-10T02:24:07.274Z","contentChangedAt":"2026-09-10T02:24:07.274Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}