apache/flink · error · IOException
Unable to deserialize default value.
Error message
Unable to deserialize default value.
What it means
During Java deserialization of the StateDescriptor (readObject), Flink rebuilds defaultValue by deserializing the stored bytes via serializer.deserialize(inView). If deserialization throws (serializer mismatch, corrupted bytes, version skew between the serializer that wrote the bytes and the one reading them), the failure is wrapped in an IOException.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/common/state/StateDescriptor.java:444
// read the default value field
boolean hasDefaultValue = in.readBoolean();
if (hasDefaultValue) {
TypeSerializer<T> serializer = serializerAtomicReference.get();
checkNotNull(serializer, "Serializer not initialized.");
int size = in.readInt();
byte[] buffer = new byte[size];
in.readFully(buffer);
try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
DataInputViewStreamWrapper inView = new DataInputViewStreamWrapper(bais)) {
defaultValue = serializer.deserialize(inView);
} catch (Exception e) {
throw new IOException("Unable to deserialize default value.", e);
}
} else {
defaultValue = null;
}
}
}
View on GitHub (pinned to 2f3c205e92)
Solutions
- Keep the TypeSerializer and the state value class stable across the checkpoint lifecycle; implement a proper TypeSerializerSnapshot with resolveSchemaCompatibility for upgrade paths.
- When changing the value class, provide a migration path (registered serializers / state processor API) before restoring from the old savepoint.
- Verify the serializer snapshot version matches (getCurrentVersion / readSnapshot) and that serialize/deserialize are symmetric in your custom serializer.
- If the default value is optional, consider removing it from the descriptor so no cross-version bytes need to survive.
Example fix
// before: restored savepoint has old MyPojoV1 default bytes, current class is MyPojoV2 // after: register a serializer snapshot upgrade path env.getConfig().registerTypeWithKryoSerializer(MyPojoV2.class, new MyPojoV2Serializer()); // and implement TypeSerializerSnapshot<MyPojoV2> with resolveSchemaCompatibility
Defensive patterns
Strategy: try-catch
Validate before calling
// Before restore, verify the serializer snapshot version is compatible
TypeSerializerSnapshot<T> snap = currentSerializer.snapshotConfiguration();
if (snap.getCurrentVersion() != expectedVersion) {
// implement a migration path before restoring
}
// Validate round-trip symmetry in tests
ByteArrayOutputStream out = new ByteArrayOutputStream();
ser.serialize(defaultValue, new DataOutputViewStreamWrapper(out));
T back = ser.deserialize(new DataInputViewStreamWrapper(
new ByteArrayInputStream(out.toByteArray())));
assert Objects.equals(defaultValue, back); Type guard
// Ensure custom serializer implements a proper snapshot with resolveSchemaCompatibility
TypeSerializerSnapshot<T> snap = serializer.snapshotConfiguration();
CompatibilityResult compat = snap.resolveSchemaCompatibility(oldSnap);
if (!compat.isCompatible()) {
// provide migration before restore
} Try / catch
// Restore failures typically surface at job startup; catch at the deploy boundary
try {
env.execute();
} catch (Exception e) {
if (e.getMessage().contains("Unable to deserialize default value")) {
// run the State Processor API to migrate the savepoint,
// or align the serializer version before retrying
}
} Prevention
- Implement TypeSerializerSnapshot for custom serializers with a correct getCurrentVersion and resolveSchemaCompatibility.
- Test savepoint restore across serializer versions in CI before upgrading.
- Keep the state value class and serializer stable; plan migrations explicitly.
When it happens
Trigger: Restoring a StateDescriptor from a checkpoint/savepoint where the default-value bytes were written by a different or older serializer; corrupted serialized descriptor bytes; a custom serializer whose read path does not match its write path.
Common situations: Upgrading the state value class or serializer after a savepoint was taken; Kryo/POJO serializer config drift between the job that wrote the descriptor and the one restoring it; transferring descriptors across Flink versions; classpath differences that load a different serializer implementation.
Related errors
- Unable to serialize default value of type {}.
- Cannot deserialize and unwrap accumulators properly.
- Corrupted data to deserialize
- The bytes are serialized with version %d, while this deseria
- Failed to create enumerator for sourceIndex={currentSourceIn
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/bdafc1e1b1553f52.
Report an issue: GitHub.