ben-manes/caffeine · error · InvalidObjectException
Proxy required
Error message
Proxy required
What it means
The synchronous view of an AsyncCache (cache.synchronous(), class LocalAsyncCache$...) rejects direct deserialization with InvalidObjectException("Proxy required") from readObject. It is designed to serialize only through writeReplace into a SyncViewProxy that carries the underlying async cache; a stream naming the view class directly would reconstruct it unsafely and is refused.
Source
Thrown at caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalAsyncCache.java:756
}
@Override
public void cleanUp() {
asyncCache().cache().cleanUp();
}
@Override
public Policy<K, V> policy() {
return asyncCache().policy();
}
@Override
public ConcurrentMap<K, V> asMap() {
return (asMapView == null) ? (asMapView = new AsMapView<>(asyncCache().cache())) : asMapView;
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
@SuppressWarnings("unused")
private void readObjectNoData() throws InvalidObjectException {
throw new InvalidObjectException("Proxy required");
}
Object writeReplace() {
return new SyncViewProxy<>(asyncCache());
}
}
final class AsMapView<K, V> implements ConcurrentMap<K, V> {
final LocalCache<K, CompletableFuture<V>> delegate;
@Nullable Set<K> keys;
@Nullable Collection<V> values;
@Nullable Set<Entry<K, V>> entries;View on GitHub (pinned to 9da6581ee3)
Solutions
- Use standard Java serialization on the public Cache/AsyncCache object so writeReplace produces the proxy
- Configure Kryo-style frameworks to honor writeReplace, or exclude Caffeine internals and ship plain entry maps
- Never subclass or reflectively instantiate the synchronous view
Example fix
// before out.writeObject(asyncCache.synchronous()); // risky: internal view target // after out.writeObject(asyncCache); // proxy-based serialization var restored = ((AsyncCache<K, V>) in.readObject()).synchronous();
Defensive patterns
Strategy: validation
Validate before calling
// Serialize the AsyncCache itself; derive the synchronous view after deserialization: out.writeObject(asyncCache); ... var syncView = ((AsyncCache<K, V>) in.readObject()).synchronous();
Try / catch
try {
Object o = in.readObject();
} catch (InvalidObjectException e) {
if ("Proxy required".equals(e.getMessage())) {
throw new IllegalStateException("Serialized a Caffeine view instead of the cache; fix the writer", e);
}
throw e;
} Prevention
- Treat cache.synchronous() views as non-serializable; serialize the AsyncCache
- Configure custom serializers to honor writeReplace or exclude Caffeine internals
- Round-trip test every serialized cache path in CI
When it happens
Trigger: Deserializing a stream whose class descriptor is the synchronous view rather than SyncViewProxy (crafted/corrupted stream, serializer that bypasses writeReplace); serializing a subclass of the view.
Common situations: Custom serialization frameworks (Kryo, FST, Jackson's Java serialization module) that reflectively instantiate classes; cluster replication of cache views; tampered or truncated payloads.
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/3b4da48bdb53705c.
Report an issue: GitHub.