stanfordnlp/CoreNLP · error · IllegalStateException

Empty node shouldn't have left branch

Error message

Empty node shouldn't have left branch

What it means

check() treats nodes with a null value ('empty' sentinel nodes) as pure placeholders: such a node must have no children. This IllegalStateException fires when an empty node still has a non-null left child, meaning a deletion or rotation left a half-detached node in the structure. It indicates internal corruption of the interval tree, not bad user data.

Solutions

  1. Rebuild the IntervalTree from scratch (new tree + re-insert all live intervals) to clear the malformed empty node.
  2. Do not keep references to removed nodes or call check()/rotate on stale node objects from a previous tree state.
  3. Serialize access to the tree (single thread or locking); it has no internal synchronization.
  4. If reproducible via public insert/remove only, file a CoreNLP bug with the minimal operation sequence.
Defensive patterns

Strategy: validation

Validate before calling

// Validate the tree after mutation-heavy phases
assertTreeValid(tree);
void assertTreeValid(IntervalTree<Interval<E>,T> t) {
  try { t.check(t.root); }
  catch (IllegalStateException e) { throw new RuntimeException("tree corrupt: " + e.getMessage()); }
}

Try / catch

try {
  tree.remove(interval, value);
} catch (IllegalStateException e) {
  tree = rebuildTree(liveIntervals);
}

Prevention

When it happens

Trigger: check() (or balance()/rotateUp() which call it) encounters TreeNode.isEmpty() == true whose left field is non-null — typically after remove() left dangling children on an emptied node.

Common situations: Hit during remove-heavy workloads (deleting many intervals then validating); seen when a custom subclass or reflection-based code touched node fields; appeared after an interrupted/concurrent mutation of a shared tree.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/util/IntervalTree.java:362

  private void adjust(TreeNode<E,T> node) {
    adjustUpwards(node, node.parent);
  }

  public void check() {
    check(root);
  }

  public void check(TreeNode<E,T> treeNode) {
    Stack<TreeNode<E,T>> todo = new Stack<>();
    todo.add(treeNode);
    while (!todo.isEmpty()) {
      TreeNode<E,T> node = todo.pop();
      if (node == node.parent) {
        throw new IllegalStateException("node is same as parent!!!");
      }
      if (node.isEmpty()) {
        if (node.left != null) throw new IllegalStateException("Empty node shouldn't have left branch");
        if (node.right != null) throw new IllegalStateException("Empty node shouldn't have right branch");
        continue;
      }
      int leftSize = (node.left != null)? node.left.size:0;
      int rightSize = (node.right != null)? node.right.size:0;
      E leftMax = (node.left != null)? node.left.maxEnd:null;
      E rightMax = (node.right != null)? node.right.maxEnd:null;
      E maxEnd = node.value.getInterval().getEnd();
      if (leftMax != null && leftMax.compareTo(maxEnd) > 0) {
        maxEnd = leftMax;
      }
      if (rightMax != null && rightMax.compareTo(maxEnd) > 0) {
        maxEnd = rightMax;
      }
      if (!maxEnd.equals(node.maxEnd)) {
        throw new IllegalStateException("max end is not as expected!!!");
      }
      if (node.size != leftSize + rightSize + 1) {

View on GitHub (pinned to 1b7edd19c4)