apache/flink · error · KryoException

Error during Java deserialization.

Error message

Error during Java deserialization.

What it means

JavaSerializer.read() is the deserialization half of the Kryo-to-Java serialization bridge. It lazily builds an InstantiationUtil.ClassLoaderObjectInputStream over Kryo's Input (using Kryo's classloader) and calls readObject(). Any failure — class not found under the user classloader, serialVersionUID mismatch, stream corruption, or a readObject method throwing — is rethrown as KryoException 'Error during Java deserialization.'.

Source

Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/kryo/JavaSerializer.java:82

        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    @Override
    public T read(Kryo kryo, Input input, Class aClass) {
        try {
            ObjectMap graphContext = kryo.getGraphContext();
            ObjectInputStream objectStream = (ObjectInputStream) graphContext.get(this);
            if (objectStream == null) {
                // make sure we use Kryo's classloader
                objectStream =
                        new InstantiationUtil.ClassLoaderObjectInputStream(
                                input, kryo.getClassLoader());
                graphContext.put(this, objectStream);
            }
            return (T) objectStream.readObject();
        } catch (Exception ex) {
            throw new KryoException("Error during Java deserialization.", ex);
        }
    }
}

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Check the nested cause: ClassNotFoundException means the class is missing from the job's user-code jar — ship it and resubmit.
  2. Add 'private static final long serialVersionUID = 1L;' to all classes stored in Java-serialized state so refactorings do not break compatibility.
  3. If the class legitimately changed incompatibly, use state processor API / savepoint migration to rewrite state, or keep a compatible readObject path.
  4. For corruption issues, verify the same serializer configuration (registered types/order) is used on write and read.

Example fix

// before
public class Event { /* no serialVersionUID */ }

// after
public class Event implements java.io.Serializable {
    private static final long serialVersionUID = 1L;
    // ...
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    T value = kryo.readObject(input, clazz);
} catch (com.esotericsoftware.kryo.KryoException e) {
    if (e.getCause() instanceof ClassNotFoundException) {
        // missing class in user classloader: fail with class name
    } else if (e.getCause() instanceof java.io.InvalidClassException) {
        // serialVersionUID mismatch: needs state migration
    }
    throw e;
}

Prevention

When it happens

Trigger: Restoring a checkpoint/savepoint where a serialized class was renamed, moved packages, or its serialVersionUID changed; the user code classloader on the TaskManager not containing the class; corrupted or truncated buffer being handed to the input stream; a custom readObject implementation throwing.

Common situations: Job upgrade without a stable serialVersionUID; classes loaded from user jars absent from the TaskManager classpath; reading state written by a different Flink or class version; network buffer truncation producing a corrupted ObjectInputStream stream.

Related errors


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