mybatis/mybatis-3 · error · CacheException

Error deserializing object. Cause: ${cause}

Error message

Error deserializing object.  Cause: ${cause}

What it means

Thrown by SerializedCache.deserialize() when converting the cached byte[] back into an object fails: class not found on deserialization (stale cache across a redeploy), stream corruption, serialVersionUID mismatch, or a JEP 290 filter rejection. Note the entry point runs SerialFilterChecker.check() first and the actual read happens inside CustomObjectInputStream, so class-loading and filter failures surface here.

Source

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

  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 {
      super(in);
    }

    @Override
    protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
      return Resources.classForName(desc.getName());
    }

  }

}

View on GitHub (pinned to 008069adb1)

Solutions

  1. Clear/flush the second-level cache after deployments that change entity classes (or version the cache keys with an app version prefix)
  2. Declare explicit serialVersionUID on cached types to survive compatible field changes
  3. Align JEP 290 filter configuration with the classes you intentionally cache; evict the offending key and retry

Example fix

// before: stale entry from previous deploy -> InvalidClassException

// after: cache keys namespaced by app version
cacheKeyPrefix = "app:v42:"; // bump on entity changes -> old entries never deserialized
Defensive patterns

Strategy: fallback

Validate before calling

null

Try / catch

catch (CacheException e) { if (e.getCause() instanceof InvalidClassException || e.getCause() instanceof ClassNotFoundException) { evict(key); return loadFromDatabaseAndRepopulate(); } throw e; }

Prevention

When it happens

Trigger: Reading a second-level cache entry (getObject) whose bytes were written by a previous deployment: entity class renamed/moved (ClassNotFoundException), class evolved without compatible serialVersionUID (InvalidClassException), cache storage (e.g. Redis/Ehcache disk) surviving application restarts, or deserialization filters blocking a class.

Common situations: Rolling deploys with a shared distributed cache where old-format entries linger; JDK upgrades tightening ObjectInputFilter defaults; repackaging entities to a new module/package; corrupted entries after a network cache hiccup.

Related errors


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