{"id":"fadfe1925ed17157","repo":"spring-projects/spring-boot","slug":"each-image-building-cache-can-be-configured-only-o","errorCode":null,"errorMessage":"Each image building cache can be configured only once","messagePattern":"Each image building cache can be configured only once","errorType":"validation","errorClass":"GradleException","httpStatus":null,"severity":"error","filePath":"build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/CacheSpec.java","lineNumber":59,"sourceCode":"\n\tprivate @Nullable Cache cache;\n\n\t@Inject\n\tpublic CacheSpec(ObjectFactory objectFactory) {\n\t\tthis.objectFactory = objectFactory;\n\t}\n\n\tpublic @Nullable Cache asCache() {\n\t\treturn this.cache;\n\t}\n\n\t/**\n\t * Configures a volume cache using the given {@code action}.\n\t * @param action the action\n\t */\n\tpublic void volume(Action<VolumeCacheSpec> action) {\n\t\tif (this.cache != null) {\n\t\t\tthrow new GradleException(\"Each image building cache can be configured only once\");\n\t\t}\n\t\tVolumeCacheSpec spec = this.objectFactory.newInstance(VolumeCacheSpec.class);\n\t\taction.execute(spec);\n\t\tthis.cache = Cache.volume(spec.getName().get());\n\t}\n\n\t/**\n\t * Configures a bind cache using the given {@code action}.\n\t * @param action the action\n\t */\n\tpublic void bind(Action<BindCacheSpec> action) {\n\t\tif (this.cache != null) {\n\t\t\tthrow new GradleException(\"Each image building cache can be configured only once\");\n\t\t}\n\t\tBindCacheSpec spec = this.objectFactory.newInstance(BindCacheSpec.class);\n\t\taction.execute(spec);\n\t\tthis.cache = Cache.bind(spec.getSource().get());\n\t}","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/spring-projects/spring-boot/blob/5b2dbdbb8be64415eb6552f81ff7c449c8d251e6/build-plugin/spring-boot-gradle-plugin/src/main/java/org/springframework/boot/gradle/tasks/bundling/CacheSpec.java#L41-L77","documentation":"Thrown by CacheSpec.volume() when the build cache has already been configured (this.cache != null). A CacheSpec models a single cache; volume/bind/image are mutually exclusive and each can be set at most once. Raised as GradleException.","triggerScenarios":"Calling buildCache { volume { } } twice, or calling buildCache { volume {...} } after buildCache { bind {...} } (or image {...}) — the second call sees this.cache already set and throws at line 59.","commonSituations":"Copy-pasting cache config blocks; a shared plugin/Convention applying cache config that the build script also sets; migrating from volume to bind cache without removing the old block.","solutions":["Configure the build cache exactly once with a single call: buildCache { volume { name = 'my-cache' } }.","Remove duplicate buildCache {} blocks contributed by other applied scripts/plugins.","If you need to switch cache type, delete the previous block first — do not stack them.","Search the build for 'buildCache'/'volume {' to find all contributors."],"exampleFix":"// before: cache configured twice\nbootBuildImage {\n    buildCache { volume { name = 'cache-a' } }\n    buildCache { volume { name = 'cache-b' } } // throws\n}\n// after: single configuration\nbootBuildImage {\n    buildCache { volume { name = 'cache-a' } }\n}\n","handlingStrategy":"validation","validationCode":"// Track cache configuration count to prevent double-config\nvar cacheConfigured = false\nfun configureVolume(name: String) {\n    check(!cacheConfigured) { \"Build cache already configured\" }\n    cacheConfigured = true\n    // ... buildCache { volume { this.name = name } }\n}\n","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Centralize cache configuration in a single place (one buildCache block).","Review applied convention plugins/init scripts for additional buildCache calls.","Never migrate cache types by stacking blocks; replace the prior one."],"tags":["gradle","boot-build-image","cache","configuration","validation"],"analyzedSha":"5b2dbdbb8be64415eb6552f81ff7c449c8d251e6","analyzedAt":"2026-08-04T18:53:14.967Z","schemaVersion":2}