{"record":{"id":"3695f5673dc59e9c","repo":"google/guava","slug":"return-value-of-s-reserialized-to-an-unequal-valu","errorCode":null,"errorMessage":"Return value of %s reserialized to an unequal value","messagePattern":"Return value of (.+?) reserialized to an unequal value","errorType":"exception","errorClass":"AssertionError","httpStatus":null,"severity":"error","filePath":"android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java","lineNumber":547,"sourceCode":"     */\n    @CanIgnoreReturnValue\n    @SuppressWarnings(\"CatchingUnchecked\") // sneaky checked exception\n    public FactoryMethodReturnValueTester testEqualsAndSerializable() throws Exception {\n      for (Invokable<?, ?> factory : getFactoriesToTest()) {\n        try {\n          testEqualsUsing(factory);\n        } catch (FactoryMethodReturnsNullException e) {\n          // If the factory returns null, we just skip it.\n        }\n        Object instance = instantiate(factory);\n        if (instance != null) {\n          try {\n            reserializeAndAssert(instance);\n          } catch (Exception e) { // sneaky checked exception\n            throw new AssertionError(\n                \"Serialization failed on return value of \" + factory, e.getCause());\n          } catch (AssertionFailedError e) {\n            throw new AssertionError(\n                \"Return value of \" + factory + \" reserialized to an unequal value\", e);\n          }\n        }\n      }\n      return this;\n    }\n\n    private ImmutableList<Invokable<?, ?>> getFactoriesToTest() {\n      ImmutableList.Builder<Invokable<?, ?>> builder = ImmutableList.builder();\n      for (Invokable<?, ?> factory : factories) {\n        if (returnTypeToTest.isAssignableFrom(factory.getReturnType().getRawType())) {\n          builder.add(factory);\n        }\n      }\n      ImmutableList<Invokable<?, ?>> factoriesToTest = builder.build();\n      Assert.assertFalse(\n          \"No \"\n              + factoryMethodsDescription","sourceCodeStart":529,"sourceCodeEnd":565,"githubUrl":"https://github.com/google/guava/blob/94f39958baf7ad51ddf9c70e406ed6b188194daa/android/guava-testlib/src/com/google/common/testing/ClassSanityTester.java#L529-L565","documentation":"Thrown by testEqualsAndSerializable() when reserializeAndAssert(instance) succeeds in deserialization but the round-tripped object is NOT equal to the original (AssertionFailedError from reserializeAndAssert). This catches broken equals/hashCode with respect to serialization: the object's identity/equality depends on data lost or changed during serialization.","triggerScenarios":"A factory returns an instance that serializes and deserializes without throwing, but the deserialized instance fails equals() against the original (e.g. hashCode mismatch, equals comparing non-serialized fields, or equals based on mutable/transient state).","commonSituations":"equals()/hashCode() using a transient or non-serialized field; a class that caches a hash or lazy field and compares it in equals; writeReplace returning a different representative object without a matching readResolve.","solutions":["Read the cause (AssertionFailedError) to see which field/property differs after round-trip.","Ensure equals()/hashCode() depend ONLY on serialized state; remove transient fields from equality, or include their underlying data in serialized form.","If writeReplace changes the representation, implement a symmetric readResolve so equals holds across the boundary."],"exampleFix":"// before\npublic final class Range implements Serializable {\n  private final int lo, hi;\n  private transient int cachedHash; // used in hashCode -> diverges after deserialization\n  public int hashCode() { return cachedHash; }\n}\n\n// after\npublic final class Range implements Serializable {\n  private final int lo, hi;\n  public int hashCode() { return lo * 31 + hi; } // computed, not cached transient\n}","handlingStrategy":"validation","validationCode":"// Round-trip equality self-check.\nObject o = factory.invoke(null);\nObject r = serializeDeserialize(o);\nif (!o.equals(r) || !r.equals(o) || o.hashCode() != r.hashCode())\n  throw new AssertionError(factory + \" not equal after reserialization\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Base equals()/hashCode() only on serialized fields; do not include transient/cached state.","If using writeReplace/readResolve, keep them symmetric so equality survives the boundary."],"tags":["guava-testlib","serialization","equals-hashcode","class-sanity"],"backgroundTag":null,"analyzedSha":"94f39958baf7ad51ddf9c70e406ed6b188194daa","analyzedAt":"2026-08-13T22:50:30.265Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}