{"record":{"id":"30db4db31b84373a","repo":"mybatis/mybatis-3","slug":"not-allowed-to-update-a-null-cache-key-instance","errorCode":null,"errorMessage":"Not allowed to update a null cache key instance.","messagePattern":"Not allowed to update a null cache key instance\\.","errorType":"exception","errorClass":"CacheException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/cache/CacheKey.java","lineNumber":38,"sourceCode":"import java.util.List;\nimport java.util.StringJoiner;\n\nimport org.apache.ibatis.reflection.ArrayUtil;\n\n/**\n * @author Clinton Begin\n */\npublic class CacheKey implements Cloneable, Serializable {\n\n  private static final long serialVersionUID = 1146682552656046210L;\n\n  public static final CacheKey NULL_CACHE_KEY = new CacheKey() {\n\n    private static final long serialVersionUID = 1L;\n\n    @Override\n    public void update(Object object) {\n      throw new CacheException(\"Not allowed to update a null cache key instance.\");\n    }\n\n    @Override\n    public void updateAll(Object[] objects) {\n      throw new CacheException(\"Not allowed to update a null cache key instance.\");\n    }\n  };\n\n  private static final int DEFAULT_MULTIPLIER = 37;\n  private static final int DEFAULT_HASHCODE = 17;\n\n  private final int multiplier;\n  private int hashcode;\n  private long checksum;\n  private int count;\n  // 8/21/2017 - Sonarlint flags this as needing to be marked transient. While true if content is not serializable, this\n  // is not always true and thus should not be marked transient.\n  private List<Object> updateList;","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/cache/CacheKey.java#L20-L56","documentation":"CacheKey.NULL_CACHE_KEY is a singleton sentinel returned by CachingExecutor/BaseJdbcStatementHandler when a statement has no cache key (notably for ResultHandler-driven or non-query flows). Calling update(Object) on it is a programming error, so the anonymous subclass overrides update() to always throw CacheException. It exists to make accidental mutation of the shared sentinel loud instead of silently corrupting cache keys for other statements.","triggerScenarios":"User code (custom Executor, interceptor, or handler) obtains a key from a context where it may be CacheKey.NULL_CACHE_KEY and calls key.update(value) on it — e.g. a plugin intercepting query() that tries to fold extra criteria into the passed-in cache key, or calling getBoundSql/update on a statement executed via a path that supplies the sentinel.","commonSituations":"Custom mybatis interceptors that augment cache keys for fine-grained caching; code copied from older mybatis versions where the passed key was always a real CacheKey; calling update on a key returned from createCacheKey() for a statement with a ResultHandler that skips caching (FlushCacheRequired / no-cache flows).","solutions":["Check for the sentinel before mutating: if (key != CacheKey.NULL_CACHE_KEY) key.update(...); or clone first","Re-examine why the key is the sentinel — for statements that bypass the cache, augmenting keys is meaningless; derive your own CacheKey instead of mutating the passed one","If writing an interceptor, create a new CacheKey from the BoundSql parameters rather than mutating the incoming one"],"exampleFix":"// before\nkey.update(extraParam);\n\n// after\nif (key != CacheKey.NULL_CACHE_KEY) {\n  key.update(extraParam);\n} else {\n  key = new CacheKey(); key.update(boundSql.getSql()); key.update(extraParam);\n}","handlingStrategy":"type-guard","validationCode":"null","typeGuard":"static boolean isMutableCacheKey(CacheKey k) { return k != null && k != CacheKey.NULL_CACHE_KEY; }","tryCatchPattern":"if (!isMutableCacheKey(key)) { key = new CacheKey(); } key.update(x); // avoid the throw by construction","preventionTips":["Never mutate executor-provided cache keys in plugins","Derive fresh CacheKeys from BoundSql when you need custom keys"],"tags":["mybatis","cache","sentinel","interceptor"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}