{"record":{"id":"0a574f08a28fa085","repo":"ben-manes/caffeine","slug":"proxy-required-0a574f","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/UnboundedLocalCache.java","lineNumber":1226,"sourceCode":"      cache = new UnboundedLocalCache<>(builder, /* isAsync= */ false);\n    }\n\n    @Override\n    public final UnboundedLocalCache<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 UnboundedPolicy<>(cache, identity);\n      }\n      return policy;\n    }\n\n    private void readObject(ObjectInputStream stream) throws InvalidObjectException {\n      throw new InvalidObjectException(\"Proxy required\");\n    }\n\n    Object writeReplace() {\n      var proxy = new SerializationProxy<K, V>();\n      proxy.isRecordingStats = cache.isRecordingStats;\n      proxy.removalListener = cache.removalListener;\n      return proxy;\n    }\n  }\n\n  /** An eviction policy that supports no bounding. */\n  static final class UnboundedPolicy<K, V> implements Policy<K, V> {\n    final Function<@Nullable V, @Nullable V> transformer;\n    final UnboundedLocalCache<K, V> cache;\n\n    UnboundedPolicy(UnboundedLocalCache<K, V> cache,\n        Function<@Nullable V, @Nullable V> transformer) {\n      this.transformer = transformer;","sourceCodeStart":1208,"sourceCodeEnd":1244,"githubUrl":"https://github.com/ben-manes/caffeine/blob/9da6581ee366aa63c51e0dc96692d02f9c29ccff/caffeine/src/main/java/com/github/benmanes/caffeine/cache/UnboundedLocalCache.java#L1208-L1244","documentation":"UnboundedLocalCache's CacheView throws InvalidObjectException(\"Proxy required\") in readObject: the unbounded cache (Caffeine.newBuilder().build() with no size/expiry limits) serializes through a dedicated SerializationProxy emitted by writeReplace, carrying only the stats flag and removal listener. Direct deserialization of the view class is refused to prevent half-constructed caches.","triggerScenarios":"A stream naming the unbounded cache view class directly instead of its SerializationProxy; serializers (Kryo/FST) that bypass writeReplace and instantiate the view; corrupted or version-mismatched streams.","commonSituations":"Persisting unbounded caches via Java serialization; custom serializers in grid/middleware products (Hazelstorm/Ignite custom interops) that reflectively build classes; cross-version cache migration.","solutions":["Serialize the Cache object returned from Caffeine.newBuilder().build() so writeReplace runs","Enable writeReplace-aware mode in your serialization framework or exclude Caffeine internals","For durability or transport, write cache.asMap() entries and rebuild the cache with the same settings on read"],"exampleFix":"// before\nout.writeObject(unboundedCacheView); // internal view\nin.readObject(); // InvalidObjectException: Proxy required\n\n// after\nout.writeObject(cache); // UnboundedLocalCache proxy path\nCache<K, V> restored = (Cache<K, V>) in.readObject();","handlingStrategy":"validation","validationCode":"// Serialize only the builder-produced cache:\nCache<K, V> cache = Caffeine.newBuilder().build(); // unbounded\nout.writeObject(cache);\n// When in doubt, snapshot entries instead:\nMap<K, V> snapshot = Map.copyOf(cache.asMap());\nout.writeObject(snapshot);","typeGuard":null,"tryCatchPattern":"try {\n  Cache<K, V> restored = (Cache<K, V>) in.readObject();\n} catch (InvalidObjectException e) {\n  if (\"Proxy required\".equals(e.getMessage())) {\n    // Writer serialized an internal view: fall back to entry-based restore\n    Map<K, V> entries = readSnapshot();\n    Cache<K, V> restored = Caffeine.newBuilder().build();\n    restored.putAll(entries);\n  }\n}","preventionTips":["Serialize the Cache from the builder, never internal views or subclasses","For unbounded caches, prefer persisting asMap() entries and rebuilding on load","Version-align Caffeine across serialization endpoints"],"tags":["caffeine","serialization","proxy-required","unbounded-cache","java-serialization"],"backgroundTag":null,"analyzedSha":"9da6581ee366aa63c51e0dc96692d02f9c29ccff","analyzedAt":"2026-08-14T14:45:28.147Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}