mybatis/mybatis-3 · error · CacheException

Error serializing object. Cause: ${cause}

Error message

Error serializing object.  Cause: ${cause}

What it means

Thrown by SerializedCache.serialize() when Java ObjectOutputStream fails while converting a cached value to bytes: the object passed the instanceof Serializable check but serialization itself errored (a nested non-serializable field, a Serializable class whose writeObject throws, or an I/O failure inside the stream). The original exception is chained as the cause and mirrored in the message.

Source

Thrown at src/main/java/org/apache/ibatis/cache/decorators/SerializedCache.java:94

  @Override
  public int hashCode() {
    return delegate.hashCode();
  }

  @Override
  public boolean equals(Object obj) {
    return delegate.equals(obj);
  }

  private byte[] serialize(Serializable value) {
    try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos)) {
      oos.writeObject(value);
      oos.flush();
      return bos.toByteArray();
    } catch (Exception e) {
      throw new CacheException("Error serializing object.  Cause: " + e, e);
    }
  }

  private Serializable deserialize(byte[] value) {
    SerialFilterChecker.check();
    Serializable result;
    try (ByteArrayInputStream bis = new ByteArrayInputStream(value);
        ObjectInputStream ois = new CustomObjectInputStream(bis)) {
      result = (Serializable) ois.readObject();
    } catch (Exception e) {
      throw new CacheException("Error deserializing object.  Cause: " + e, e);
    }
    return result;
  }

  public static class CustomObjectInputStream extends ObjectInputStream {

    public CustomObjectInputStream(InputStream in) throws IOException {

View on GitHub (pinned to 008069adb1)

Solutions

  1. Inspect the cause: NotSerializableException names the exact offending class — make that class Serializable or mark the field transient
  2. Remove non-data fields from cached result types, or map them with a typeHandler that stores a serializable representation
  3. Check JEP 290 serialization filters / SerialFilterChecker constraints if classes are being rejected by policy

Example fix

// before
public class Order implements Serializable {
  private transient OrderStateListener listener; // was NOT transient -> NotSerializableException
}

// after
public class Order implements Serializable {
  private transient OrderStateListener listener;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight in tests: round-trip the entity
new ObjectOutputStream(new ByteArrayOutputStream()).writeObject(sampleEntity); // throws NotSerializableException naming the field type

Type guard

static boolean safelySerializable(Object o) { try { serialize(o); return true; } catch (Exception e) { return false; } }

Try / catch

catch (CacheException e) { if (e.getCause() instanceof NotSerializableException) { mark type non-cacheable / disable cache for that mapper and log; } else throw e; }

Prevention

When it happens

Trigger: Caching an object whose top-level class implements Serializable but a field's type does not (NotSerializableException deep in the graph); a custom writeObject/readObject throwing; graph cycles or StackOverflowError during serialization of deeply nested lazy-loaded proxies.

Common situations: Entities with lazy-loading proxies (mybatis lazy loading wrapped objects) whose enhancer state is not serializable; adding a new field of a library type (e.g. java.lang.Thread, an InputStream wrapper) to a cached entity; serialization filters (JEP 290) rejecting a class in newer JDKs.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/879640aa30ebf2fc. Report an issue: GitHub.