Thomas Keller
09/01/2026, 7:33 AMbuild.gradle.kts , so applied via the plugins { ... } block and (b) via project-specific build conventions, that apply the plugin and preconfigure it.
Now it can happen that the JAR of one plugin of the shared build conventions is applied via the project-specific build conventions and directly in the build.gradle.kts (of course, different plugins) and in this case Gradle tells me
Error resolving plugin [id: 'plugin.applied.in.build.gradle.kts', version: '0.1.0']
> The request for this plugin could not be satisfied because the plugin is already on the classpath with an unknown version, so compatibility cannot be checked.
so I remove the version identifier from the plugins {} block. But then for another build convention plugin, that I apply on the root build, I get
Plugin [id: 'other.plugin.applied.in.root.build.gradle.kts'] was not found in any of the following sources:
- Gradle Core Plugins (plugin is not in 'org.gradle' namespace)
- Included Builds (None of the included builds contain this plugin)
- Plugin Repositories (plugin dependency must include a version number for this source)
even though the root build has project-specific build conventions applied before as well, which carry the JAR as a dependency as well. So I'm kind of puzzled why some plugins {} blocks need a version and others must not get one. I feel I'm doing something wrong.Jean Helou
09/01/2026, 7:42 AM// <https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_management>
// > The pluginManagement {} block may only appear in either the settings.gradle
// > file, where it must be the first block in the file, or in an Initialization
// > Script.
pluginManagement {
val ourPluginVersion: String = providers.gradleProperties("ourPluginVersion")
val openapiPluginVersion: String = providers.gradleProperties("openapiPluginVersion")
// /!\ Make sure plugins are loaded in the root build.gradle.kts, with `apply false` if not
// used. This will ensure Gradle loads them only once, which saved a few seconds of
// configuration.
plugins {
id("com.sample.api-conventions") version ourPluginVersion
id("com.sample.app-conventions") version ourPluginVersion
id("com.sample.common-conventions") version ourPluginVersion
id("com.sample.contract-first") version ourPluginVersion
id("com.sample.contract-first-client") version ourPluginVersion
id("com.sample.contract-first-common") version ourPluginVersion
id("com.sample.contract-first-ws") version ourPluginVersion
id("com.sample.idea-exclusion-project") version ourPluginVersion
id("com.sample.idea-exclusion-root") version ourPluginVersion
id("com.sample.jooq-generation") version ourPluginVersion
id("com.sample.library-conventions") version ourPluginVersion
id("com.sample.lombok-conventions") version ourPluginVersion
id("com.sample.mjml-components") version ourPluginVersion
id("com.sample.mjml-emails") version ourPluginVersion
id("com.sample.no-dep-app-conventions") version ourPluginVersion
id("com.sample.openapi-contract") version ourPluginVersion
id("com.sample.phase-duration") version ourPluginVersion
id("com.sample.project-description") version ourPluginVersion
id("com.sample.sonar") version ourPluginVersion
id("com.sample.typescript-generation") version ourPluginVersion
id("org.springdoc.openapi-gradle-plugin") version openapiPluginVersion
}
then in our build.gradle.kts we apply the plugins but never provide an explicit version.Thomas Keller
09/01/2026, 7:51 AMpluginManagement {
resolutionStrategy {
eachPlugin {
if (requested.id.namespace?.startsWith("my.plugin.prefix") == true) {
useModule("my.group:my.artifact:$myBuildLogicVersion")
}
}
}
}
but the problem is that this pluginManagement only works for plugins applied to build scripts, if you apply the plugin in a build convention itself (which is a build by itself) this block is not executed, so you have to define "my.group:..." in the dependencies block of that build. Thinking about it, maybe one needs a combination of both, only having a compilyOnly dependency in the build convention build and then the above thing for the actual application of the plugins.Vampire
09/01/2026, 8:44 AMVampire
09/01/2026, 8:46 AMJean Helou
09/01/2026, 8:54 AMThe problem is that this pluginManagement only works for plugins applied to build scripts, if you apply the plugin in a build convention itself (which is a build by itself) this block is not executed, so you have to define "my.group:..." in the dependencies block of that buildhmm in my example
com.sample.common-conventions 's definition contains
plugins {
`java-library`
kotlin("jvm")
kotlin("plugin.spring")
id("com.sample.idea-exclusion-project")
id("com.sample.jacoco")
id("com.sample.project-description")
}
and we do have the dependencies declared same as for external plugins ....
dependencies {
implementation(project(":conventions:jacoco-convention"))
implementation(project(":conventions:sonar-convention"))
implementation(project(":idea-exclusion-plugin"))
implementation(buildLibs.git.properties.gradle.plugin)
implementation(buildLibs.spring.boot.gradle.plugin)
implementation(buildLibs.kotlin.gradle.plugin)
implementation(buildLibs.kotlin.allopen)
implementation(buildLibs.kotlin.jvm.plugin)
implementation(buildLibs.kotlin.spring.plugin)
}
Our plugins build is itself a multiproject build so we don't need to explicit the versions there, all our plugins are always released together.
We only enforce the versions for the build scripts and thus have a single version to pull.Vampire
09/01/2026, 9:04 AMplugins { ... } block, but for example as dependency in buildSrc.
But also if you bring the jar to the classpath with one plugin id and then apply another plugin id.
For example if you have kotlin("jvm") version "1.6.10" apply false in the settings script and _kotlin_("android") _version_ "1.6.10" _apply_ false in the build script, or one in a parent build script and the other in a child build script.
Because Gradle does not know about the actual code artifact being the same here, but it is about plugin ID marker artifacts.
As the two are different plugin IDs but result to the same code JAR, the higher one already adds the plugin of the lower one to the classpath but not knowing about the plugin id.
I'd say this has room for improvement in Gradle, so that where the higher plugin is added to the classpath it recognizes all plugin IDs in it as added with a known version, so that it would properly complain if a different version is used and support it if the same version is used.Vampire
09/01/2026, 9:07 AMVampire
09/01/2026, 9:08 AMapply false if there none should be applied and use no version everywhere else.