stanfordnlp/CoreNLP · error · RuntimeException

oldTag starts with B, entity at position should not be null

Error message

oldTag starts with B, entity at position should not be null

What it means

EntityCachingAbstractSequencePriorBIO maintains a cache of BIO entities parallel to the tag sequence. updateSequenceElement validates internal consistency: if the changed tag was a 'B-' tag, an entity must exist at that position; if the cache is null, the cache is corrupt and it throws a RuntimeException.

Solutions

  1. Ensure the label scheme is consistent BIO (every B- has a following I- or is a single-token entity) and that entities are built by the class's own setup
  2. Verify the sequence passed to scoresOf comes from the same reader/label scheme used to build the cache
  3. Report/inspect with a debugger: entities[position] null while classIndex.get(oldVal) starts with 'B' indicates external mutation
  4. If you modified updateSequenceElement or the cache-building code, restore it or rebuild entities[position] before updating

Example fix

// before: sequence labels use B- but cache built assuming IO scheme
prior.scoresOf(doc, sequence); // RuntimeException
// after: use the matching BIO reader/labels so cache includes entities
List<CoreLabel> doc = bioReader.processDocument(text); // builds consistent B-/I- tags
prior.scoresOf(doc, sequence);
Defensive patterns

Strategy: validation

Validate before calling

// ensure sequence uses the same BIO label scheme the prior was built with
String firstTag = classIndex.get(sequence[0]);
if (firstTag != null && firstTag.startsWith("B-"))
  throw new IllegalStateException("B- labels require entity cache built via BIO reader");

Try / catch

try {
  scores = prior.scoresOf(doc, sequence);
} catch (RuntimeException e) {
  if (e.getMessage().contains("entity at position should not be null"))
    log.error("entity cache desync — rebuild sequence via the same label scheme");
  throw e;
}

Prevention

When it happens

Trigger: Calling scoresOf/sequence mutation paths where the entity cache was not built consistently with the tag sequence — e.g. a sequence containing B- tags whose entity cache was never populated, or mutated through an unexpected path.

Common situations: Custom DocumentReaderAndWriter or label schemes feeding B- labels without matching entity construction; modifying the label index or sequence outside the supported API; mismatched BIO vs IO label encodings.

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/dc7e4483b789bf11. Report an issue: GitHub.

Appendix: source

Thrown at src/edu/stanford/nlp/ie/EntityCachingAbstractSequencePriorBIO.java:228

    return false;
  }

  @Override
  public void updateSequenceElement(int[] sequence, int position, int oldVal) {
    this.sequence = sequence;

    if (sequence[position] == oldVal)
      return;

    if (VERBOSE) log.info("changing position "+position+" from " +classIndex.get(oldVal)+" to "+classIndex.get(sequence[position]));

    if (sequence[position] == backgroundSymbol) { // new tag is O
      String oldRawTag = classIndex.get(oldVal);
      String[] oldParts = oldRawTag.split("-");
      if (oldParts[0].equals("B")) { // old tag was a B, current entity definitely affected, also check next one
        EntityBIO entity = entities[position];
        if (entity == null)
          throw new RuntimeException("oldTag starts with B, entity at position should not be null");
        // remove entities for all words affected by this entity
        for (int i=0; i < entity.words.size(); i++) {
          entities[position+i] = null;
        }
      } else { // old tag was a I, check previous one
        if (entities[position] != null) { // this was part of an entity, shortened
          if (VERBOSE) log.info("splitting off prev entity");
          EntityBIO oldEntity = entities[position];
          int oldLen = oldEntity.words.size();
          int offset = position - oldEntity.startPosition;
          List<String> newWords = new ArrayList<>();
          for (int i=0; i<offset; i++) {
            newWords.add(oldEntity.words.get(i));
          }
          oldEntity.words = newWords;
          oldEntity.otherOccurrences = otherOccurrences(oldEntity);
          // need to clean any remaining entity
          for (int i=0 ; i < oldLen - offset; i++) {

View on GitHub (pinned to 1b7edd19c4)