{"record":{"id":"3b4da48bdb53705c","repo":"ben-manes/caffeine","slug":"proxy-required-3b4da4","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/LocalAsyncCache.java","lineNumber":756,"sourceCode":"    }\n\n    @Override\n    public void cleanUp() {\n      asyncCache().cache().cleanUp();\n    }\n\n    @Override\n    public Policy<K, V> policy() {\n      return asyncCache().policy();\n    }\n\n    @Override\n    public ConcurrentMap<K, V> asMap() {\n      return (asMapView == null) ? (asMapView = new AsMapView<>(asyncCache().cache())) : asMapView;\n    }\n\n    private void readObject(ObjectInputStream stream) throws InvalidObjectException {\n      throw new InvalidObjectException(\"Proxy required\");\n    }\n\n    @SuppressWarnings(\"unused\")\n    private void readObjectNoData() throws InvalidObjectException {\n      throw new InvalidObjectException(\"Proxy required\");\n    }\n\n    Object writeReplace() {\n      return new SyncViewProxy<>(asyncCache());\n    }\n  }\n\n  final class AsMapView<K, V> implements ConcurrentMap<K, V> {\n    final LocalCache<K, CompletableFuture<V>> delegate;\n\n    @Nullable Set<K> keys;\n    @Nullable Collection<V> values;\n    @Nullable Set<Entry<K, V>> entries;","sourceCodeStart":738,"sourceCodeEnd":774,"githubUrl":"https://github.com/ben-manes/caffeine/blob/9da6581ee366aa63c51e0dc96692d02f9c29ccff/caffeine/src/main/java/com/github/benmanes/caffeine/cache/LocalAsyncCache.java#L738-L774","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom serialization frameworks (Kryo, FST, Jackson's Java serialization module) that reflectively instantiate classes; cluster replication of cache views; tampered or truncated payloads.","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"],"exampleFix":"// before\nout.writeObject(asyncCache.synchronous()); // risky: internal view target\n\n// after\nout.writeObject(asyncCache); // proxy-based serialization\nvar restored = ((AsyncCache<K, V>) in.readObject()).synchronous();","handlingStrategy":"validation","validationCode":"// Serialize the AsyncCache itself; derive the synchronous view after deserialization:\nout.writeObject(asyncCache);\n...\nvar syncView = ((AsyncCache<K, V>) in.readObject()).synchronous();","typeGuard":null,"tryCatchPattern":"try {\n  Object o = in.readObject();\n} catch (InvalidObjectException e) {\n  if (\"Proxy required\".equals(e.getMessage())) {\n    throw new IllegalStateException(\"Serialized a Caffeine view instead of the cache; fix the writer\", e);\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["caffeine","serialization","proxy-required","synchronous-view","async-cache"],"backgroundTag":null,"analyzedSha":"9da6581ee366aa63c51e0dc96692d02f9c29ccff","analyzedAt":"2026-08-14T14:45:28.147Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}