{"record":{"id":"32374779744c9d32","repo":"stanfordnlp/CoreNLP","slug":"can-only-operate-on-preterminals","errorCode":null,"errorMessage":"Can only operate on preterminals","messagePattern":"Can only operate on preterminals","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"src/edu/stanford/nlp/trees/international/french/FrenchTreeNormalizer.java","lineNumber":70,"sourceCode":"      }\n    };\n  }\n\n  @Override\n  public String normalizeTerminal(String terminal) {\n    if(terminal == null) return terminal;\n\n    return super.normalizeTerminal(terminal).intern();\n  }\n\n  @Override\n  public String normalizeNonterminal(String category) {\n    return super.normalizeNonterminal(category).intern();\n  }\n\n  private static void replacePOSTag(Tree t, MorphoFeatureSpecification morpho) {\n    if (!t.isPreTerminal()) {\n      throw new IllegalArgumentException(\"Can only operate on preterminals\");\n    }\n\n    if (!(t.label() instanceof CoreLabel)) {\n      throw new IllegalArgumentException(\"Only operates on CoreLabels\");\n    }\n    CoreLabel label = (CoreLabel) t.label();\n\n    Tree child = t.children()[0];\n    if (!(child.label() instanceof CoreLabel)) {\n      throw new IllegalArgumentException(\"Only operates on CoreLabels\");\n    }\n    CoreLabel childLabel = (CoreLabel) child.label();\n\n    // Morphological Analysis\n    String morphStr = childLabel.originalText();\n    if (morphStr == null || morphStr.equals(\"\")) {\n      morphStr = label.value();\n      // POS subcategory","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/stanfordnlp/CoreNLP/blob/1b7edd19c4d0d7b1f13a2591425b9b60a0b1af7a/src/edu/stanford/nlp/trees/international/french/FrenchTreeNormalizer.java#L52-L88","documentation":"replacePOSTag in FrenchTreeNormalizer rewrites the POS tag of a French tree node using MorphoFeatureSpecification, but it only accepts preterminal (POS-level) nodes whose labels are CoreLabel instances. The library throws IllegalArgumentException immediately when a node of any other shape is passed, because the downstream cast to CoreLabel and tag replacement would otherwise fail or corrupt the tree.","triggerScenarios":"Calling replacePOSTag (directly or via normalizePreterminal) with a Tree whose !t.isPreTerminal() — e.g. a full sentence tree, an intermediate constituent like an NP/S, or a leaf word node — instead of a POS-tagged preterminal like (NN chat).","commonSituations":"Developers running the French morphological normalizer over a whole parsed tree without descending to preterminals first; using a custom TreeLabel or LabeledScoredTreeNode with String labels instead of CoreLabel; loading trees from a parser that doesn't use CoreLabel factories.","solutions":["Traverse the tree and call normalizePreterminal only on preterminal nodes: check t.isPreTerminal() before invoking it","Ensure the tree's labels are CoreLabel, e.g. build trees with CoreLabel factory or copy labels: label.setFromString(...) on a CoreLabel","Run tree.transform(new FrenchTreeNormalizer(...)) / normalizeWholeTrees so the library itself iterates the correct node types","Log or skip non-preterminal nodes instead of forcing them through the normalizer"],"exampleFix":"// before\nnormalizer.normalizePreterminal(sentenceTree, morpho); // throws: not a preterminal\n\n// after\nif (sentenceTree.isPreTerminal()) {\n  normalizer.normalizePreterminal(sentenceTree, morpho);\n} else {\n  for (Tree pre : sentenceTree) {\n    if (pre.isPreTerminal()) normalizer.normalizePreterminal(pre, morpho);\n  }\n}","handlingStrategy":"validation","validationCode":"// caller-side check\nif (tree == null || !tree.isPreTerminal())\n  throw new IllegalArgumentException(\"normalizePreterminal requires a preterminal tree\");","typeGuard":"static boolean isPreTerminalWithCoreLabel(Tree t) {\n  return t != null && t.isPreTerminal() && t.label() instanceof CoreLabel;\n}","tryCatchPattern":"try {\n  normalizer.normalizePreterminal(tree, morpho);\n} catch (IllegalArgumentException e) {\n  log.warn(\"Skipping non-preterminal/non-CoreLabel node: \" + e.getMessage());\n}","preventionTips":["Only call normalizePreterminal inside a preterminal-filtered traversal (t.isPreTerminal())","Use the library's normalizeWholeTrees/transform entry points instead of node-level calls","Build French trees with CoreLabel-producing readers (FrenchTreeReaderFactory)","Assert label types in your tree-loading unit tests"],"tags":["java","nlp","trees","precondition"],"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-15T23:17:13.987Z"}