{"record":{"id":"d107d1944012c148","repo":"stanfordnlp/CoreNLP","slug":"called-headpreterminal-on-a-leaf","errorCode":null,"errorMessage":"Called headPreTerminal on a leaf: ","messagePattern":"Called headPreTerminal on a leaf: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/trees/Tree.java","lineNumber":1139,"sourceCode":"    return headTerminal(hf, null);\n  }\n\n\n  /**\n   * Returns the preterminal tree that is the head of the tree.\n   * See {@link #isPreTerminal()} for\n   * the definition of a preterminal node. Beware that some tree nodes may\n   * have no preterminal head.\n   *\n   * @param hf The headfinding algorithm to use\n   * @return The head preterminal tree, if any, else {@code null}\n   * @throws IllegalArgumentException if called on a leaf node\n   */\n  public Tree headPreTerminal(HeadFinder hf) {\n    if (isPreTerminal()) {\n      return this;\n    } else if (isLeaf()) {\n      throw new IllegalArgumentException(\"Called headPreTerminal on a leaf: \" + this);\n    } else {\n      Tree head = hf.determineHead(this);\n      if (head != null) {\n        return head.headPreTerminal(hf);\n      }\n      log.info(\"Head preterminal is null: \" + this);\n      return null;\n    }\n  }\n\n  /**\n   * Finds the head words of each tree and assigns\n   * HeadWordLabelAnnotation on each node pointing to the correct\n   * CoreLabel.  This relies on the nodes being CoreLabels, so it\n   * throws an IllegalArgumentException if this is ever not true.\n   */\n  public void percolateHeadAnnotations(HeadFinder hf) {\n    if (!(label() instanceof CoreLabel)) {","sourceCodeStart":1121,"sourceCodeEnd":1157,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/trees/Tree.java#L1121-L1157","documentation":"headPreTerminal(HeadFinder) walks down from a node to its head preterminal (the head's parent of leaf). If called on a leaf node there is no preterminal below it, so the method throws IllegalArgumentException including the offending tree in the message.","triggerScenarios":"Calling tree.headPreTerminal(headFinder) directly on a leaf Tree, or recursion reaching a leaf whose head path bottoms out at a leaf (e.g. determineHead returns a child that is itself a leaf).","commonSituations":"Feature-extraction loops that iterate all nodes including leaves and call headPreTerminal on each; head-finder rules misconfigured so heads resolve to leaves rather than preterminals.","solutions":["Guard with isPreTerminal() or !isLeaf() before calling headPreTerminal","Use a different accessor (e.g. headTerminal(hf) or the node itself) when starting from a leaf","Fix HeadFinder rules if determineHead is returning leaves instead of preterminals"],"exampleFix":"// before\nTree p = node.headPreTerminal(hf);\n// after\nif (!node.isLeaf()) { Tree p = node.headPreTerminal(hf); } else { Tree p = node; }","handlingStrategy":"type-guard","validationCode":"if (node.isLeaf()) throw new IllegalStateException(\"headPreTerminal is undefined for leaves\");","typeGuard":"Tree headPreTerminalSafe(Tree t, HeadFinder hf) { return t.isLeaf() ? t : (t.isPreTerminal() ? t : t.headPreTerminal(hf)); }","tryCatchPattern":"try { return node.headPreTerminal(hf); } catch (IllegalArgumentException e) { log.warn(\"leaf passed to headPreTerminal\"); return node; }","preventionTips":["Skip leaves when iterating nodes for head features","Use headTerminal(hf) when a leaf result is acceptable","Validate HeadFinder rules return preterminals, not leaves"],"tags":["validation","nlp","head-finder"],"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"}