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
- 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
- Verify the sequence passed to scoresOf comes from the same reader/label scheme used to build the cache
- Report/inspect with a debugger: entities[position] null while classIndex.get(oldVal) starts with 'B' indicates external mutation
- 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
- Do not mutate label sequences outside the class's updateSequenceElement path
- Use one consistent BIO/IO tagging scheme end-to-end
- Don't hand-edit classIndex or entity arrays
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
- adjustFinalToken: Unexpected final char: |
- Shouldn't happen:
- Error reading saved links
- RuntimeIOException wrapping IOException
- Error creating data exporter
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)