ben-manes/caffeine · error · InvalidObjectException
Proxy required
Error message
Proxy required
What it means
UnboundedLocalCache's CacheView throws InvalidObjectException("Proxy required") in readObject: the unbounded cache (Caffeine.newBuilder().build() with no size/expiry limits) serializes through a dedicated SerializationProxy emitted by writeReplace, carrying only the stats flag and removal listener. Direct deserialization of the view class is refused to prevent half-constructed caches.
Source
Thrown at caffeine/src/main/java/com/github/benmanes/caffeine/cache/UnboundedLocalCache.java:1226
cache = new UnboundedLocalCache<>(builder, /* isAsync= */ false);
}
@Override
public final UnboundedLocalCache<K, V> cache() {
return cache;
}
@Override
public final Policy<K, V> policy() {
if (policy == null) {
Function<@Nullable V, @Nullable V> identity = v -> v;
policy = new UnboundedPolicy<>(cache, identity);
}
return policy;
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
Object writeReplace() {
var proxy = new SerializationProxy<K, V>();
proxy.isRecordingStats = cache.isRecordingStats;
proxy.removalListener = cache.removalListener;
return proxy;
}
}
/** An eviction policy that supports no bounding. */
static final class UnboundedPolicy<K, V> implements Policy<K, V> {
final Function<@Nullable V, @Nullable V> transformer;
final UnboundedLocalCache<K, V> cache;
UnboundedPolicy(UnboundedLocalCache<K, V> cache,
Function<@Nullable V, @Nullable V> transformer) {
this.transformer = transformer;View on GitHub (pinned to 9da6581ee3)
Solutions
- Serialize the Cache object returned from Caffeine.newBuilder().build() so writeReplace runs
- Enable writeReplace-aware mode in your serialization framework or exclude Caffeine internals
- For durability or transport, write cache.asMap() entries and rebuild the cache with the same settings on read
Example fix
// before out.writeObject(unboundedCacheView); // internal view in.readObject(); // InvalidObjectException: Proxy required // after out.writeObject(cache); // UnboundedLocalCache proxy path Cache<K, V> restored = (Cache<K, V>) in.readObject();
Defensive patterns
Strategy: validation
Validate before calling
// Serialize only the builder-produced cache: Cache<K, V> cache = Caffeine.newBuilder().build(); // unbounded out.writeObject(cache); // When in doubt, snapshot entries instead: Map<K, V> snapshot = Map.copyOf(cache.asMap()); out.writeObject(snapshot);
Try / catch
try {
Cache<K, V> restored = (Cache<K, V>) in.readObject();
} catch (InvalidObjectException e) {
if ("Proxy required".equals(e.getMessage())) {
// Writer serialized an internal view: fall back to entry-based restore
Map<K, V> entries = readSnapshot();
Cache<K, V> restored = Caffeine.newBuilder().build();
restored.putAll(entries);
}
} Prevention
- Serialize the Cache from the builder, never internal views or subclasses
- For unbounded caches, prefer persisting asMap() entries and rebuilding on load
- Version-align Caffeine across serialization endpoints
When it happens
Trigger: A stream naming the unbounded cache view class directly instead of its SerializationProxy; serializers (Kryo/FST) that bypass writeReplace and instantiate the view; corrupted or version-mismatched streams.
Common situations: Persisting unbounded caches via Java serialization; custom serializers in grid/middleware products (Hazelstorm/Ignite custom interops) that reflectively build classes; cross-version cache migration.
Related errors
- Proxy required
- Proxy required
- asyncCache required
- throw new ExceptionInInitializerError(e)
- An invalid state was detected, occurring when the key's equa
AI-assisted analysis of ben-manes/caffeine@9da6581ee3 (2026-08-14).
Data as JSON: /api/errors/0a574f08a28fa085.
Report an issue: GitHub.