apache/flink · error · IllegalStateException
Concurrent access to KryoSerializer. Thread 1: {threadName1}
Error message
Concurrent access to KryoSerializer. Thread 1: {threadName1} , Thread 2: {threadName2} What it means
KryoSerializer is not thread-safe; it tracks the current accessing thread with a best-effort check (enterExclusiveThread/exitExclusiveThread) around serialize/deserialize. If serialize() or deserialize() is entered while a different thread is already recorded (previous != null && previous != thisThread), it throws IllegalStateException naming both threads. Note the check is deliberately non-atomic, so genuine races may instead produce corrupted Kryo state rather than this exception.
Source
Thrown at flink-core/src/main/java/org/apache/flink/api/java/typeutils/runtime/kryo/KryoSerializer.java:706
"Could not clone serializer instance of class " + original.getClass(), ex);
}
}
// --------------------------------------------------------------------------------------------
// For testing
// --------------------------------------------------------------------------------------------
private void enterExclusiveThread() {
// we use simple get, check, set here, rather than CAS
// we don't need lock-style correctness, this is only a sanity-check and we thus
// favor speed at the cost of some false negatives in this check
Thread previous = currentThread;
Thread thisThread = Thread.currentThread();
if (previous == null) {
currentThread = thisThread;
} else if (previous != thisThread) {
throw new IllegalStateException(
"Concurrent access to KryoSerializer. Thread 1: "
+ thisThread.getName()
+ " , Thread 2: "
+ previous.getName());
}
}
private void exitExclusiveThread() {
currentThread = null;
}
@VisibleForTesting
public Kryo getKryo() {
checkKryoInitialized();
return this.kryo;
}
}
View on GitHub (pinned to 2f3c205e92)
Solutions
- Give each thread its own serializer: call serializer.duplicate() (TypeSerializer API) per thread instead of sharing one instance.
- Remove static/shared caching of the TypeSerializer — obtain it per subtask/per thread from the TypeInformation.
- Synchronize external access if duplication is impossible: wrap serialize/deserialize calls in a lock so only one thread enters at a time.
- In custom Kryo serializers, never re-enter the outer serializer; use the Kryo instance passed to the callback.
Example fix
// before private static final TypeSerializer<MyEvent> SER = TypeInfoFactory...createSerializer(); // shared across pool threads -> IllegalStateException // after private final TypeSerializer<MyEvent> ser = baseSerializer.duplicate(); // one per thread/subtask
Defensive patterns
Strategy: validation
Validate before calling
// Before sharing, give each thread its own copy — TypeSerializer.duplicate() exists for this. TypeSerializer<T> threadLocalSer = baseSer.duplicate();
Prevention
- Treat TypeSerializer instances as thread-confined; never cache them in static fields.
- Call duplicate() whenever a serializer crosses a thread boundary (pools, async sinks, snapshot threads).
- Remember the check is best-effort: absence of this exception does not prove thread safety.
When it happens
Trigger: Two threads calling serialize()/deserialize() on the SAME KryoSerializer instance concurrently; sharing one serializer instance across parallel subtasks, a thread pool, or an async snapshotting thread plus the mailbox thread; re-entrant serialization from a custom Kryo serializer that itself uses the same KryoSerializer.
Common situations: Caching a TypeSerializer in a static/instance field used by multiple worker threads in an application or in a source/sink that serializes from an I/O thread; reusing serializers across operator subtasks instead of duplicating them; using the same KryoSerializer from a Kafka callback thread and the main pipeline thread.
Related errors
- Error during Java serialization.
- Error during Java deserialization.
- The Kryo Output still contains data from a previous serializ
- Could not clone serializer instance of class {className}
- Unrecognized Kryo registration serializer definition type: {
AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14).
Data as JSON: /api/errors/97b04584a88e1d81.
Report an issue: GitHub.