eclipse-vertx/vert.x · error · IllegalArgumentException
Invalid type for shareddata data structure: ${obj.getClass()
Error message
Invalid type for shareddata data structure: ${obj.getClass().getName()} What it means
Vert.x SharedData only allows values that can be safely shared across threads/cluster members. Checker.checkType validates that an object placed into a shared data structure is Serializable, Shareable, or ClusterSerializable; otherwise it throws IllegalArgumentException naming the offending class.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/shareddata/impl/Checker.java:57
.add(String.class)
.add(Integer.class)
.add(Long.class)
.add(Boolean.class)
.add(Double.class)
.add(Float.class)
.add(Short.class)
.add(Byte.class)
.add(Character.class)
.add(BigInteger.class)
.add(BigDecimal.class)
.build()
.collect(toSet());
static void checkType(Object obj) {
Objects.requireNonNull(obj, "null not allowed for shareddata data structure");
// All immutables and byte arrays are Serializable by the platform
if (!(obj instanceof Serializable || obj instanceof Shareable || obj instanceof ClusterSerializable)) {
throw new IllegalArgumentException("Invalid type for shareddata data structure: " + obj.getClass().getName());
}
}
@SuppressWarnings("unchecked")
static <T> T copyIfRequired(T obj) {
Object result;
if (obj == null) {
// Happens with putIfAbsent
result = null;
} else if (IMMUTABLE_TYPES.contains(obj.getClass())) {
result = obj;
} else if (obj instanceof byte[]) {
result = copyByteArray((byte[]) obj);
} else if (obj instanceof Shareable) {
result = ((Shareable) obj).copy();
} else if (obj instanceof ClusterSerializable) {
result = copyClusterSerializable((ClusterSerializable) obj);
} else if (obj instanceof Serializable) {View on GitHub (pinned to fb308bd8c3)
Solutions
- Make the stored class implement Serializable (or Shareable for mutable types with an efficient copy strategy).
- If the type is an array-like payload, store byte[] or a Buffer (Buffer is ClusterSerializable).
- Store a serializable representation (JSON via JsonObject, a String, or a primitive wrapper) instead of the raw object.
- If the object must stay non-serializable, keep it in an ordinary ConcurrentMap rather than SharedData.
Example fix
// before
localMap.put("cfg", new MyConfig()); // MyConfig not serializable -> IllegalArgumentException
// after
public class MyConfig implements Serializable { ... }
localMap.put("cfg", new MyConfig()); Defensive patterns
Strategy: validation
Validate before calling
static boolean isShareableType(Object o) {
return o instanceof java.io.Serializable
|| o instanceof io.vertx.core.shareddata.Shareable
|| o instanceof io.vertx.core.cluster.ClusterSerializable;
}
if (!isShareableType(value)) throw new IllegalArgumentException("cannot store " + value.getClass() + " in shared data"); Type guard
static boolean isShareableType(Object o) {
return o instanceof java.io.Serializable
|| o instanceof io.vertx.core.shareddata.Shareable
|| o instanceof io.vertx.core.cluster.ClusterSerializable;
} Try / catch
try {
map.put(key, value);
} catch (IllegalArgumentException e) {
if (e.getMessage().startsWith("Invalid type")) {
throw new IllegalStateException("Value type " + value.getClass().getName() + " is not shareable");
}
throw e;
} Prevention
- Make every domain class stored in shared data implement Serializable or Shareable at definition time.
- Prefer Buffer/JsonObject/boxed primitives which are already supported.
- Add a unit test that puts each stored type into a LocalMap.
- Never assume lambda/graph-heavy objects (streams, builders) are shareable.
When it happens
Trigger: Calling put/putForEviction on a LocalMap or an AsyncMap with a key or value whose class implements none of java.io.Serializable, io.vertx.core.shareddata.Shareable, or io.vertx.core.cluster.ClusterSerializable (e.g. a POJO without implements Serializable).
Common situations: Developers put arbitrary domain objects (config beans, builders like java.lang.Object graphs, non-serializable library types) into a LocalMap or cluster-wide AsyncMap; or after a refactor the value class lost its Serializable interface.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Invalid type: ${obj.getClass().getName()} to put in async ma
- ttl must be positive: ${ttl}
- Cannot put null in key or value of async map
- Can't register a system codec
- Cannot open file for neither reading nor writing
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/3c48395cd000a7bf.
Report an issue: GitHub.