stanfordnlp/CoreNLP · error · RuntimeException

Label dictionary is already locked.

Error message

Label dictionary is already locked.

What it means

LabelDictionary supports a two-phase lifecycle: collect observation counts via increment(), then freeze them via lock(). Once lock() has run, labelDictionary is non-null and no further observations may be recorded, because the constrained label sets are already materialized. Calling increment after that point throws this RuntimeException.

Solutions

  1. Create a new LabelDictionary for each collection phase; call lock() only after all increment() calls are done.
  2. Restructure the pipeline so lock() is the last step before CRF training begins.
  3. If you need to add data after locking, rebuild the dictionary: new LabelDictionary, re-increment everything, lock again.

Example fix

// before
dict.increment("foo", "LOC"); // after lock() already ran -> throws

// after
LabelDictionary dict2 = new LabelDictionary(); // fresh instance for new data
dict2.increment("foo", "LOC");
dict2.lock(threshold, labelIndex);
Defensive patterns

Strategy: try-catch

Validate before calling

// Java: guard before incrementing
boolean locked;
try {
  java.lang.reflect.Field f = LabelDictionary.class.getDeclaredField("labelDictionary");
  f.setAccessible(true);
  locked = f.get(dict) != null;
} catch (Exception e) { locked = false; }
if (!locked) dict.increment(observation, label);

Try / catch

// Java
try {
  dict.increment(observation, label);
} catch (RuntimeException e) {
  if (e.getMessage().contains("already locked")) {
    dict = new LabelDictionary(); // restart collection phase
    dict.increment(observation, label);
  } else throw e;
}

Prevention

When it happens

Trigger: Calling increment(observation, label) after lock(threshold, labelIndex) has already been called on the same LabelDictionary instance — e.g. training a second dataset with a dictionary that was locked for the first, or calling increment inside an loop that runs after lock.

Common situations: Reusing a LabelDictionary across multiple training runs; pipeline code that calls lock() for CRF feature setup and then continues accumulating observations; adding new training data to an already-locked dictionary.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/ie/crf/LabelDictionary.java:60

  private int[][] labelDictionary;

  /**
   * Constructor.
   */
  public LabelDictionary() {
    this.observationCounts = new ClassicCounter<>(DEFAULT_CAPACITY);
    this.observedLabels = Generics.newHashMap(DEFAULT_CAPACITY);
  }

  /**
   * Increment counts for an observation/label pair.
   *
   * @param observation
   * @param label
   */
  public void increment(String observation, String label) {
    if (labelDictionary != null) {
      throw new RuntimeException("Label dictionary is already locked.");
    }
    observationCounts.incrementCount(observation);
    if ( ! observedLabels.containsKey(observation)) {
      observedLabels.put(observation, new HashSet<>());
    }
    observedLabels.get(observation).add(label.intern());
  }

  /**
   * True if this observation is constrained, and false otherwise.
   */
  public boolean isConstrained(String observation) {
    return observationIndex.indexOf(observation) >= 0;
  }

  /**
   * Get the allowed label set for an observation.
   *

View on GitHub (pinned to 1b7edd19c4)