stanfordnlp/CoreNLP · error · RuntimeIOException

Should have FeatureFactory but got

Error message

Should have FeatureFactory but got 

What it means

While deserializing a classifier, CRFClassifier reads the serialized feature factory objects; since the 2014 format stores a count then each FeatureFactory instance. If an element read back is not an instanceof FeatureFactory, it throws RuntimeIOException('Should have FeatureFactory but got <class>'). This means the stream's feature-factory section is corrupted or was written by an incompatible version/class layout.

Solutions

  1. Check the classpath for duplicate/conflicting stanford jars and keep a single consistent version that matches the model.
  2. Verify the model file integrity (size, checksum) and re-download/re-serialize it.
  3. Use the same Stanford NLP release to read the model as the one that wrote it.
  4. If a custom FeatureFactory was used at training time, ensure it is on the classpath and extends FeatureFactory.
  5. Reserialize the classifier with the current version (load with old version, then serializeClassifier) to migrate formats.

Example fix

// before
classpath: stanford-corenlp-3.9.2.jar:stanford-classifier-4.0.0.jar  // mixed versions
// after
classpath: stanford-corenlp-4.0.0.jar  // single consistent version matching the model
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect duplicate/conflicting Stanford jars before loading
Set<String> seen = new HashSet<>();
for (URL url : ((URLClassLoader) CRFClassifier.class.getClassLoader()).getURLs())
  if (url.getPath().matches(".*(stanford-.*|classifier|corenlp).*jar") && !seen.add(new File(url.getPath()).getName()))
    throw new IllegalStateException("Duplicate Stanford jars on classpath: " + seen);

Try / catch

try {
  crf.loadClassifier(modelFile, props);
} catch (RuntimeIOException e) {
  if (String.valueOf(e.getMessage()).startsWith("Should have FeatureFactory"))
    throw new IllegalStateException("Model/classpath version mismatch — align Stanford NLP jar version with the model", e);
  throw e;
}

Prevention

When it happens

Trigger: loadClassifier / loadClassifierFromObjectStream on a serialized classifier whose featureFactory slot deserializes to a wrong class — e.g. model serialized with different NERFeatureFactory classes, classpath containing a conflicting Stanford NLP version, or corrupted/truncated stream.

Common situations: Multiple stanford-corenlp/stanford-classifier jars on the classpath causing wrong FeatureFactory class to load; deserializing a model from a very different library version; corrupted model file; custom feature factory not extending FeatureFactory at serialization time.

Related errors


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

Appendix: source

Thrown at src/edu/stanford/nlp/ie/crf/CRFClassifier.java:2588

    if (featureFactory instanceof List) {
      featureFactories = ErasureUtils.uncheckedCast(featureFactories);
//      int i = 0;
//      for (FeatureFactory ff : featureFactories) { // XXXX
//        System.err.println("List FF #" + i + ": " + ((NERFeatureFactory) ff).describeDistsimLexicon()); // XXXX
//        i++;
//      }
    } else if (featureFactory instanceof FeatureFactory) {
      featureFactories = Generics.newArrayList();
      featureFactories.add((FeatureFactory<IN>) featureFactory);
//      System.err.println(((NERFeatureFactory) featureFactory).describeDistsimLexicon()); // XXXX
    } else if (featureFactory instanceof Integer) {
      // this is the current format (2014) since writing list didn't work (see note in serializeClassifier).
      int size = (Integer) featureFactory;
      featureFactories = Generics.newArrayList(size);
      for (int i = 0; i < size; ++i) {
        featureFactory = ois.readObject();
        if (!(featureFactory instanceof FeatureFactory)) {
          throw new RuntimeIOException("Should have FeatureFactory but got " + featureFactory.getClass());
        }
//        System.err.println("FF #" + i + ": " + ((NERFeatureFactory) featureFactory).describeDistsimLexicon()); // XXXX
        featureFactories.add((FeatureFactory<IN>) featureFactory);
      }
    }

    // log.info("properties passed into CRF's loadClassifier are:" + props);
    if (props != null) {
      flags.setProperties(props, false);
    }

    windowSize = ois.readInt();
    Object tempWeights = ois.readObject();
    if (tempWeights instanceof double[][]) {
      // TODO: if slow, maybe use some temp variables for the arrays
      double[][] dWeights = (double[][]) tempWeights;
      weights = new float[dWeights.length][];
      for (int i = 0; i < dWeights.length; ++i) {

View on GitHub (pinned to 1b7edd19c4)