stanfordnlp/CoreNLP · error · IllegalStateException

Classes derived from AbstractCollinsHeadFinder must create…

Error message

Classes derived from AbstractCollinsHeadFinder must create and fill HashMap nonTerminalInfo.

What it means

AbstractCollinsHeadFinder.determineHead requires the subclass to have populated the nonTerminalInfo map defining head rules per nonterminal category; if it is null the head finder is unconfigured and IllegalStateException is thrown. It catches subclasses that never initialized their grammar-specific rules.

Solutions

  1. Initialize nonTerminalInfo = new HashMap<>() in the subclass constructor and populate rules for every relevant nonterminal category (percolate default rules via super)
  2. Reuse an existing configured head finder (e.g. CollinsHeadFinder, SemanticHeadFinder) instead of a bare subclass
  3. Add a unit test that calls determineHead on a small tree to fail fast at construction time
  4. If subclassing, call a shared init method from every constructor

Example fix

// before
public MyHeadFinder(TreebankLanguagePack tlp) {
  super(tlp);
}
// after
public MyHeadFinder(TreebankLanguagePack tlp) {
  super(tlp);
  nonTerminalInfo = new HashMap<>();
  nonTerminalInfo.put("S", new String[][] {{"left", "NP", "VP"}});
  nonTerminalInfo.put("default", new String[][] {{"right", "NN"}});
}
Defensive patterns

Strategy: validation

Validate before calling

Field f = AbstractCollinsHeadFinder.class.getDeclaredField("nonTerminalInfo"); f.setAccessible(true); if (f.get(headFinder) == null) { throw new IllegalStateException("head finder not initialized"); }

Type guard

boolean isConfigured(HeadFinder hf) { return hf instanceof AbstractCollinsHeadFinder && nonTerminalInfoInitialized((AbstractCollinsHeadFinder) hf); }

Try / catch

try { Tree head = headFinder.determineHead(tree); } catch (IllegalStateException e) { log.error("head finder subclass missing nonTerminalInfo rules"); }

Prevention

When it happens

Trigger: Instantiating a subclass of AbstractCollinsHeadFinder whose constructor did not create and fill nonTerminalInfo (e.g. writing a custom head finder without defining rules), then calling determineHead / percolateHeads.

Common situations: Custom head finder implementations for a new treebank or language that forgot rule definitions; subclasses constructed via reflection where the initializing constructor was bypassed.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/trees/AbstractCollinsHeadFinder.java:161

  public Tree determineHead(Tree t) {
    return determineHead(t, null);
  }

  /**
   * Determine which daughter of the current parse tree is the head.
   *
   * @param t The parse tree to examine the daughters of.
   *          If this is a leaf, {@code null} is returned
   * @param parent The parent of t
   * @return The daughter parse tree that is the head of {@code t}.
   *   Returns null for leaf nodes.
   * @see Tree#percolateHeads(HeadFinder)
   *      for a routine to call this and spread heads throughout a tree
   */
  @Override
  public Tree determineHead(Tree t, Tree parent) {
    if (nonTerminalInfo == null) {
      throw new IllegalStateException("Classes derived from AbstractCollinsHeadFinder must create and fill HashMap nonTerminalInfo.");
    }
    if (t == null || t.isLeaf()) {
      throw new IllegalArgumentException("Can't return head of null or leaf Tree.");
    }
    if (DEBUG) {
      log.info("determineHead for " + t.value());
    }

    Tree[] kids = t.children();

    Tree theHead;
    // first check if subclass found explicitly marked head
    if ((theHead = findMarkedHead(t)) != null) {
      if (DEBUG) {
        log.info("Find marked head method returned " +
                           theHead.label() + " as head of " + t.label());
      }
      return theHead;

View on GitHub (pinned to 1b7edd19c4)