apache/flink · critical · IOException
The class {} is not a subclass of {}
Error message
The class {} is not a subclass of {} What it means
After successfully loading the class named in a checkpoint's metadata via Class.forName, InstantiationUtil.resolveClassByName verifies it is a subtype of the expected supertype (e.g. TypeSerializerSnapshot). If supertype.isAssignableFrom(rawClazz) is false, this IOException is thrown, meaning the stored class exists but does not implement/extend what the restore code requires.
Source
Thrown at flink-core/src/main/java/org/apache/flink/util/InstantiationUtil.java:673
final String className = in.readUTF();
final Class<?> rawClazz;
try {
rawClazz = Class.forName(className, false, cl);
} catch (ClassNotFoundException e) {
String error = "Could not find class '" + className + "' in classpath.";
if (className.contains("SerializerConfig")) {
error +=
" TypeSerializerConfigSnapshot and it's subclasses are not supported since Flink 1.17."
+ " If you are using built-in serializers, please first migrate to Flink 1.16."
+ " If you are using custom serializers, please migrate them to"
+ " TypeSerializerSnapshot using Flink 1.16.";
}
throw new IOException(error, e);
}
if (!supertype.isAssignableFrom(rawClazz)) {
throw new IOException(
"The class " + className + " is not a subclass of " + supertype.getName());
}
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) rawClazz;
return clazz;
}
// --------------------------------------------------------------------------------------------
/** Private constructor to prevent instantiation. */
private InstantiationUtil() {
throw new RuntimeException();
}
}
View on GitHub (pinned to 2f3c205e92)
Solutions
- Read the two class names in the message: the stored class and the expected supertype, then check why the subtype relation is broken (usually the removed TypeSerializerConfigSnapshot API).
- If upgrading across 1.17: perform an intermediate restore on Flink 1.16 with serializers already migrated to TypeSerializerSnapshot, then take a new savepoint.
- Fix the custom class so it implements the expected interface (e.g. TypeSerializerSnapshot) and rebuild the user jar.
- Check for duplicate/legacy jars on the classpath that shadow the correct snapshot class.
Example fix
// before
public class MySnapshot extends SimpleTypeSerializerSnapshot<MyType> {} // but class in jar v1 implements TypeSerializerConfigSnapshot
// after: ensure the snapshot class on the runtime classpath implements the required supertype
public class MySnapshot extends SimpleTypeSerializerSnapshot<MyType> {
public MySnapshot() { super(MySerializer::new); }
} Defensive patterns
Strategy: validation
Validate before calling
Class<?> stored = Class.forName(snapshotClassName, false, cl);
if (!TypeSerializerSnapshot.class.isAssignableFrom(stored)) {
throw new IllegalStateException(snapshotClassName + " does not implement TypeSerializerSnapshot; checkpoint format predates Flink 1.17 - migrate via 1.16");
} Prevention
- Version checkpoints explicitly and refuse restore across incompatible major versions
- Run restore in a staging job first when upgrading Flink
- Avoid keeping legacy serializer jars that shadow new implementations
When it happens
Trigger: Restoring a checkpoint/savepoint where the serializer snapshot class name resolves to an existing class, but that class does not implement the required interface — typically an old TypeSerializerConfigSnapshot subclass being checked against TypeSerializerSnapshot, or a refactored class that changed its type hierarchy.
Common situations: Flink upgrade from 1.x to 1.17+ where the old snapshot class is still on the classpath but implements the removed API; a user refactored a custom serializer snapshot so it no longer implements TypeSerializerSnapshot; two versions of a class in different jars and the wrong one wins classloading.
Related errors
- Could not find class '{}' in classpath. TypeSerializerConfig
- Cannot deserialize and unwrap accumulators properly.
- Failed to serialize ExecutionPlan.
- Failed to deserialize coordination response
- Cannot deserialize and unwrap accumulators properly.
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/fc693b183ffc0a31.
Report an issue: GitHub.