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

  1. Keep the TypeSerializer and the state value class stable across the checkpoint lifecycle; implement a proper TypeSerializerSnapshot with resolveSchemaCompatibility for upgrade paths.
  2. When changing the value class, provide a migration path (registered serializers / state processor API) before restoring from the old savepoint.
  3. Verify the serializer snapshot version matches (getCurrentVersion / readSnapshot) and that serialize/deserialize are symmetric in your custom serializer.
  4. 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

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


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/bdafc1e1b1553f52. Report an issue: GitHub.