ben-manes/caffeine · error · InvalidObjectException
Proxy required
Error message
Proxy required
What it means
BoundedLocalCache's LoadingCacheView (the view returned by cache.asLoadingCache()/asMap-related serializable views) refuses direct deserialization by throwing InvalidObjectException("Proxy required") from its private readObject. These views serialize exclusively through a serialization proxy (writeReplace), so a stream that targets the view class directly instead of the proxy is rejected to prevent constructing partially-initialized, unsafe cache views.
Source
Thrown at caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java:4272
cache = LocalCacheFactory.newBoundedLocalCache(builder, loader, /* isAsync= */ false);
}
@Override
public final BoundedLocalCache<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 BoundedPolicy<>(cache, identity, cache.isWeighted);
}
return policy;
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
private Object writeReplace() {
return makeSerializationProxy(cache);
}
}
@SuppressWarnings({"NullableOptional",
"OptionalAssignedToNull", "OptionalUsedAsFieldOrParameterType"})
static final class BoundedPolicy<K, V> implements Policy<K, V> {
final Function<@Nullable V, @Nullable V> transformer;
final BoundedLocalCache<K, V> cache;
final boolean isWeighted;
@Nullable Optional<Eviction<K, V>> eviction;
@Nullable Optional<FixedRefresh<K, V>> refreshes;
@Nullable Optional<FixedExpiration<K, V>> afterWrite;
@Nullable Optional<FixedExpiration<K, V>> afterAccess;View on GitHub (pinned to 9da6581ee3)
Solutions
- Serialize the top-level Cache/LoadingCache object, never an internally-obtained view; its writeReplace emits the proxy automatically
- Do not subclass or reflectively instantiate Caffeine's internal view classes
- If using Kryo or similar, register Caffeine's serialization proxies or fall back to copying entries in/out of a new cache instead of serializing internals
Example fix
// before (conceptual)
out.writeObject(cache.asLoadingCache()); // risky internal view
...
in.readObject(); // InvalidObjectException: Proxy required
// after
out.writeObject(cache); // serializes via its proxy
...
@SuppressWarnings("unchecked")
Cache<K, V> cache = (Cache<K, V>) in.readObject(); Defensive patterns
Strategy: validation
Validate before calling
// Validate that you serialize the public cache type, not an internal view: Object target = (view instanceof Cache<?, ?>) ? view : cache; // always prefer `cache` out.writeObject(target);
Try / catch
try {
Object o = in.readObject();
} catch (InvalidObjectException e) {
if ("Proxy required".equals(e.getMessage())) {
throw new IllegalStateException(
"Stream targets a Caffeine internal view; re-serialize the Cache itself", e);
}
throw e;
} Prevention
- Serialize the Cache/LoadingCache object returned by build(), never views from asMap()/asLoadingCache()
- Do not subclass Caffeine internal classes
- Keep Caffeine versions identical on both ends of serialized streams
When it happens
Trigger: Deserializing an object stream that names the view class directly (hand-crafted or corrupted stream); subclassing the cache view and serializing the subclass, which bypasses writeReplace; using an outdated stream format from an incompatible Caffeine version.
Common situations: Round-tripping caches through Java serialization across Caffeine versions; custom serialization frameworks (Kryo, ObjectMapper with Java serialization) that reflectively instantiate the view instead of honoring writeReplace; corrupted payloads in a cache-replication layer.
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/974ca6265f667afe.
Report an issue: GitHub.