{"record":{"id":"52f03220afe3bde2","repo":"stanfordnlp/CoreNLP","slug":"headindex-of-headindex-is-out-of-bounds-for-a","errorCode":null,"errorMessage":"-headIndex of ${headIndex} is out of bounds for a phrase with ${nodes.size()} words","messagePattern":"-headIndex of (.+?) is out of bounds for a phrase with (.+?) words","errorType":"exception","errorClass":"SsurgeonParseException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/semgraph/semgrex/ssurgeon/SetPhraseHead.java","lineNumber":35,"sourceCode":" * If that condition is matched, then existing internal edges are replaced with edges to the new head, with the given reln <br>\n * If the head is changed, the edge out of the phrase (if it is not root) is changed to come from the new head <br>\n * Edges in to the phrase are also changed to point to the new head.\n * The purpose of that change is so for a noun phrase, for example, modifiers of that noun phrase such as nmod or nmod:desc now modify the new head\n */\npublic class SetPhraseHead extends SsurgeonEdit {\n  public static final String LABEL = \"setPhraseHead\";\n\n  final List<String> phrase;\n  final int headIndex;\n  final GrammaticalRelation relation;\n  final double weight;\n\n  public SetPhraseHead(List<String> nodes, Integer headIndex, GrammaticalRelation relation, double weight) {\n    if (headIndex == null) {\n      throw new SsurgeonParseException(\"SetPhraseHead expected a -headIndex, 0-indexed for the node to use as the new head\");\n    }\n    if (headIndex < 0 || headIndex >= nodes.size()) {\n      throw new SsurgeonParseException(\"-headIndex of \" + headIndex + \" is out of bounds for a phrase with \" + nodes.size() + \" words\");\n    }\n\n    if (relation == null) {\n      throw new SsurgeonParseException(\"SetPhraseHead expected a -reln to represent the dependency to use for the new phrase\");\n    }\n\n    this.phrase = new ArrayList<>(nodes);\n    this.headIndex = headIndex;\n    this.relation = relation;\n    this.weight = weight;\n  }\n\n  @Override\n  public String toEditString() {\n    StringWriter buf = new StringWriter();\n    buf.write(LABEL);\n    buf.write(\"\\t\");\n    for (String node : phrase) {","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/semgraph/semgrex/ssurgeon/SetPhraseHead.java#L17-L53","documentation":"SetPhraseHead validates that headIndex is a 0-indexed position within the supplied node list; values below 0 or at/after nodes.size() point at no node in the phrase and the constructor throws with the actual index and list size in the message.","triggerScenarios":"new SetPhraseHead(nodes, headIndex, ...) where headIndex < 0 or headIndex >= nodes.size(), e.g. headIndex 2 with a 2-node list, or an Ssurgeon line with -headIndex 1 when only one -node was given.","commonSituations":"Confusing 1-based and 0-based indexing in rules; adding/removing -node arguments from the rule without updating -headIndex; generating rules programmatically where the node list shrunk.","solutions":["Set -headIndex to a value between 0 and (number of -node arguments - 1)","Count the -node entries in the rule and adjust headIndex; remember it is 0-indexed","Update headIndex whenever you add or remove nodes from the phrase"],"exampleFix":"// before\nnew SetPhraseHead(Arrays.asList(\"n1\",\"n2\"), 2, reln, 1.0); // out of bounds\n// after\nnew SetPhraseHead(Arrays.asList(\"n1\",\"n2\"), 1, reln, 1.0);","handlingStrategy":"validation","validationCode":"if (headIndex != null && (headIndex < 0 || headIndex >= nodes.size())) {\n  throw new IllegalArgumentException(\"headIndex \" + headIndex + \" must be in [0, \" + (nodes.size()-1) + \"]\");\n}","typeGuard":"boolean headIndexInRange(Integer idx, int numNodes) { return idx != null && idx >= 0 && idx < numNodes; }","tryCatchPattern":"try {\n  op = new SetPhraseHead(nodes, headIndex, relation, weight);\n} catch (SsurgeonParseException e) {\n  log.error(\"-headIndex out of bounds: {}\", e.getMessage());\n  throw new RuleSyntaxException(e);\n}","preventionTips":["Remember headIndex is 0-based; the last valid value is nodeCount - 1","Recompute -headIndex whenever -node arguments change","Add a rule-lint step that parses every set-phrase-head line and checks bounds"],"tags":["java","ssurgeon","index-out-of-range"],"backgroundTag":"index-out-of-range","analyzedSha":"1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a","analyzedAt":"2026-09-10T02:24:07.274Z","contentChangedAt":"2026-09-10T02:24:07.274Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}