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

  1. Use standard Java serialization on the public Cache/AsyncCache object so writeReplace produces the proxy
  2. Configure Kryo-style frameworks to honor writeReplace, or exclude Caffeine internals and ship plain entry maps
  3. 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

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


AI-assisted analysis of ben-manes/caffeine@9da6581ee3 (2026-08-14). Data as JSON: /api/errors/3b4da48bdb53705c. Report an issue: GitHub.