{"record":{"id":"0c842cecee6c4d2a","repo":"mybatis/mybatis-3","slug":"invalid-cache-decorator-cache-decorators-mu","errorCode":null,"errorMessage":"Invalid cache decorator ({}).  Cache decorators must have a constructor that takes a Cache instance as a parameter.  Cause: {}","messagePattern":"Invalid cache decorator \\((.+?)\\)\\.  Cache decorators must have a constructor that takes a Cache instance as a parameter\\.  Cause: (.+?)","errorType":"validation","errorClass":"CacheException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/mapping/CacheBuilder.java","lineNumber":214,"sourceCode":"          + \"Base cache implementations must have a constructor that takes a String id as a parameter.  Cause: \" + e,\n          e);\n    }\n  }\n\n  private Cache newCacheDecoratorInstance(Class<? extends Cache> cacheClass, Cache base) {\n    Constructor<? extends Cache> cacheConstructor = getCacheDecoratorConstructor(cacheClass);\n    try {\n      return cacheConstructor.newInstance(base);\n    } catch (Exception e) {\n      throw new CacheException(\"Could not instantiate cache decorator (\" + cacheClass + \"). Cause: \" + e, e);\n    }\n  }\n\n  private Constructor<? extends Cache> getCacheDecoratorConstructor(Class<? extends Cache> cacheClass) {\n    try {\n      return cacheClass.getConstructor(Cache.class);\n    } catch (Exception e) {\n      throw new CacheException(\"Invalid cache decorator (\" + cacheClass + \").  \"\n          + \"Cache decorators must have a constructor that takes a Cache instance as a parameter.  Cause: \" + e, e);\n    }\n  }\n}\n","sourceCodeStart":196,"sourceCodeEnd":219,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/mapping/CacheBuilder.java#L196-L219","documentation":"CacheBuilder.getCacheDecoratorConstructor() throws CacheException when a class used as a cache decorator has no public constructor taking a single Cache instance. MyBatis decorator contract requires Decorator(Cache delegate); the message names the offending class and chains the reflection failure.","triggerScenarios":"Adding a custom decorator via CacheBuilder.addDecorator(MyDecorator.class) or the standard decorator mechanism when MyDecorator lacks a public Cache-taking constructor (only no-arg, or extra parameters).","commonSituations":"Writing a decorator modeled on other frameworks' wrappers (no delegate parameter); non-public constructors; accidentally registering a base cache class (String-id constructor) as a decorator.","solutions":["Give the decorator a public constructor: public MyDecorator(Cache delegate) { this.delegate = delegate; }.","Check you registered the right kind of class: base caches take String, decorators take Cache.","Make the constructor public so reflection can invoke it."],"exampleFix":"// before\npublic class MyDecorator implements Cache {\n  public MyDecorator() {} // missing Cache delegate parameter\n}\n\n// after\npublic class MyDecorator implements Cache {\n  private final Cache delegate;\n  public MyDecorator(Cache delegate) { this.delegate = delegate; }\n}","handlingStrategy":"validation","validationCode":"// Assert decorator contract before adding it\ntry {\n  MyDecorator.class.getConstructor(Cache.class);\n} catch (NoSuchMethodException e) {\n  throw new IllegalStateException(\"MyDecorator must expose public MyDecorator(Cache delegate)\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model custom decorators on MyBatis' own decorators: single public (Cache delegate) constructor.","Never register a base cache (String-id constructor) as a decorator or vice versa.","Reflection-smoke-test all cache-related classes in a startup self-check."],"tags":["mybatis","cache","decorator","constructor-contract"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}