eclipse-vertx/vert.x · error · IllegalArgumentException
Cannot put null in key or value of async map
Error message
Cannot put null in key or value of async map
What it means
SharedDataImpl.checkType rejects null keys or values before they reach an async map: async maps cannot store null entries because keys/values must be serializable and Comparable across the cluster. It throws IllegalArgumentException with this message.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/shareddata/impl/SharedDataImpl.java:142
@SuppressWarnings("unchecked")
@Override
public <K, V> Future<AsyncMap<K, V>> getLocalAsyncMap(String name) {
LocalAsyncMapImpl<K, V> asyncMap = (LocalAsyncMapImpl<K, V>) localAsyncMaps.computeIfAbsent(name, n -> new LocalAsyncMapImpl<>(vertx));
ContextInternal context = vertx.getOrCreateContext();
return context.succeededFuture(new WrappedAsyncMap<>(asyncMap));
}
@Override
public Future<Counter> getLocalCounter(String name) {
Counter counter = localCounters.computeIfAbsent(name, n -> new AsynchronousCounter(vertx));
ContextInternal context = vertx.getOrCreateContext();
return context.succeededFuture(counter);
}
private static void checkType(Object obj) {
if (obj == null) {
throw new IllegalArgumentException("Cannot put null in key or value of async map");
}
// All immutables and byte arrays are Serializable by the platform
if (!(obj instanceof Serializable || obj instanceof ClusterSerializable)) {
throw new IllegalArgumentException("Invalid type: " + obj.getClass().getName() + " to put in async map");
}
}
public static final class WrappedAsyncMap<K, V> implements AsyncMap<K, V> {
private final AsyncMap<K, V> delegate;
WrappedAsyncMap(AsyncMap<K, V> other) {
this.delegate = other;
}
@Override
public Future<V> get(K k) {
checkType(k);View on GitHub (pinned to fb308bd8c3)
Solutions
- Check key/value for null before calling any map method and handle the null case explicitly.
- Provide a non-null default or sentinel value instead of storing null.
- Fix the upstream producer so the value is never null (Objects.requireNonNull at creation).
Example fix
// before
String val = config.getString("opt");
map.put("k", val); // NPE risk / IllegalArgumentException when val == null
// after
String val = config.getString("opt");
if (val != null) map.put("k", val); Defensive patterns
Strategy: validation
Validate before calling
if (key == null || value == null) {
throw new IllegalArgumentException("async map key/value must be non-null");
} Type guard
static <V> java.util.Optional<V> nonNullValue(V v) { return java.util.Optional.ofNullable(v); } Try / catch
try {
map.put(key, value);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Cannot put null")) {
map.put(key, defaultValue);
} else throw e;
} Prevention
- Wrap nullable producer results in Optional and only put when present.
- Use Objects.requireNonNull early where values are created, not at storage time.
- Never store null as a tombstone; use remove(key) instead.
When it happens
Trigger: Calling put/putIfAbsent/get/replace on an AsyncMap or cluster-wide map with a null key or null value.
Common situations: A lookup returned null and its result was passed straight to map.put; optional config values (null when unset) used as values; deserialized JSON fields defaulting to null.
Related errors
- ttl must be positive: ${ttl}
- Invalid type: ${obj.getClass().getName()} to put in async ma
- Invalid type for shareddata data structure: ${obj.getClass()
- Can't get cluster wide map if not clustered
- Can't register a system codec
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/e4da922c5e096e04.
Report an issue: GitHub.