{"record":{"id":"974ca6265f667afe","repo":"ben-manes/caffeine","slug":"proxy-required","errorCode":null,"errorMessage":"Proxy required","messagePattern":"Proxy required","errorType":"exception","errorClass":"InvalidObjectException","httpStatus":null,"severity":"error","filePath":"caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java","lineNumber":4272,"sourceCode":"      cache = LocalCacheFactory.newBoundedLocalCache(builder, loader, /* isAsync= */ false);\n    }\n\n    @Override\n    public final BoundedLocalCache<K, V> cache() {\n      return cache;\n    }\n\n    @Override\n    public final Policy<K, V> policy() {\n      if (policy == null) {\n        Function<@Nullable V, @Nullable V> identity = v -> v;\n        policy = new BoundedPolicy<>(cache, identity, cache.isWeighted);\n      }\n      return policy;\n    }\n\n    private void readObject(ObjectInputStream stream) throws InvalidObjectException {\n      throw new InvalidObjectException(\"Proxy required\");\n    }\n\n    private Object writeReplace() {\n      return makeSerializationProxy(cache);\n    }\n  }\n\n  @SuppressWarnings({\"NullableOptional\",\n    \"OptionalAssignedToNull\", \"OptionalUsedAsFieldOrParameterType\"})\n  static final class BoundedPolicy<K, V> implements Policy<K, V> {\n    final Function<@Nullable V, @Nullable V> transformer;\n    final BoundedLocalCache<K, V> cache;\n    final boolean isWeighted;\n\n    @Nullable Optional<Eviction<K, V>> eviction;\n    @Nullable Optional<FixedRefresh<K, V>> refreshes;\n    @Nullable Optional<FixedExpiration<K, V>> afterWrite;\n    @Nullable Optional<FixedExpiration<K, V>> afterAccess;","sourceCodeStart":4254,"sourceCodeEnd":4290,"githubUrl":"https://github.com/ben-manes/caffeine/blob/9da6581ee366aa63c51e0dc96692d02f9c29ccff/caffeine/src/main/java/com/github/benmanes/caffeine/cache/BoundedLocalCache.java#L4254-L4290","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before (conceptual)\nout.writeObject(cache.asLoadingCache()); // risky internal view\n...\nin.readObject(); // InvalidObjectException: Proxy required\n\n// after\nout.writeObject(cache);            // serializes via its proxy\n...\n@SuppressWarnings(\"unchecked\")\nCache<K, V> cache = (Cache<K, V>) in.readObject();","handlingStrategy":"validation","validationCode":"// Validate that you serialize the public cache type, not an internal view:\nObject target = (view instanceof Cache<?, ?>) ? view : cache; // always prefer `cache`\nout.writeObject(target);","typeGuard":null,"tryCatchPattern":"try {\n  Object o = in.readObject();\n} catch (InvalidObjectException e) {\n  if (\"Proxy required\".equals(e.getMessage())) {\n    throw new IllegalStateException(\n        \"Stream targets a Caffeine internal view; re-serialize the Cache itself\", e);\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["caffeine","serialization","proxy-required","invalid-object","java-serialization"],"backgroundTag":null,"analyzedSha":"9da6581ee366aa63c51e0dc96692d02f9c29ccff","analyzedAt":"2026-08-14T14:45:28.147Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}