A build fails with a plugin version conflict (the same plugin requested at two different versions). How do you diagnose and fix it?
answer
- same id, two versions on classpath
- grep settings/catalog/root/modules
- single source of truth
- alias + apply false pattern
- not a dependency resolutionStrategy
basics
~10 sFind every place the plugin's version is declared (catalog, settings pluginManagement, root and module plugins blocks). Keep exactly one declaration as the source of truth, omit the version everywhere else, and re-run the build.
solid answer
~40 sPlugin version conflicts happen when the same plugin id is requested with two different versions on the build script classpath — e.g. a catalog alias in one module and a literal `version "x"` in another, or a root `apply false` at one version and a module overriding it. Diagnose by searching the build for the plugin id across `settings.gradle.kts` (pluginManagement.plugins), `gradle/libs.versions.toml`, the root build script, and module scripts; the error message usually names the conflicting requests. Fix by enforcing a single source of truth — typically the version catalog or `pluginManagement.plugins {}` — declaring the version exactly once and applying by bare id/alias everywhere else. Then run a clean build. To prevent recurrence, lint scripts for stray literal versions and centralize all plugin coordinates.
code
bash · 7 lines# Find every place a plugin version is declared
grep -rn "org.springframework.boot" \
settings.gradle.kts build.gradle.kts \
gradle/libs.versions.toml \
$(find . -name 'build.gradle.kts')
# Then keep ONE version (catalog), omit it elsewhere, and:
./gradlew help --rerun-tasksgo deeper
Recall that the same plugin shouldn't be declared at two versions.
Locate all declarations and consolidate to one, applying by id/alias elsewhere.
Explain why it's not a dependency-strategy fix and add prevention (lint, centralization).
Institute org-wide policy: published catalog + CI enforcement so conflicts can't land.
## Why the conflict occurs Gradle resolves plugins onto a single build script classpath. Each plugin **id** may carry **one** version request there. If two requests for the same id specify *different* versions, Gradle cannot pick silently and fails the build with a message like *"Plugin request for plugin 'X' already on the classpath with a different version"* (wording varies by version). Typical causes: - A version-catalog alias in module A and a hard-coded `id("x") version "..."` in module B. - Root declares `id("x") version "1.0" apply false`, a module re-declares `version "2.0"`. - `pluginManagement.plugins {}` pins one version while a script overrides it. - An included build (`build-logic`) brings a transitive plugin version that clashes with the consumer. ## Diagnosis steps 1. **Read the error** — it names the plugin id and (often) both versions/requests. 2. **Grep the whole build** for the id: `settings.gradle.kts`, `libs.versions.toml`, root + module build scripts, `build-logic`. 3. **Inspect the catalog** — confirm the `[plugins]` entry and its `version.ref`. 4. Optionally run `./gradlew :module:dependencies` style introspection isn't for plugins, but `--info`/`--stacktrace` on the failing configuration phase surfaces which requests collided. ## The fix: one source of truth ```kotlin // BAD — two different versions // root build.gradle.kts plugins { id("org.springframework.boot") version "3.2.0" apply false } // module build.gradle.kts plugins { id("org.springframework.boot") version "3.3.0" } // conflict! ``` ```kotlin // GOOD — declare once (catalog), apply by alias // libs.versions.toml: [plugins] spring-boot = { id="org.springframework.boot", version.ref="springBoot" } // root build.gradle.kts plugins { alias(libs.plugins.springBoot) apply false } // module build.gradle.kts plugins { alias(libs.plugins.springBoot) } // no version ``` ## Prevention - **Centralize**: catalog or `pluginManagement.plugins {}` only; modules apply by id/alias. - **Lint**: a CI grep or custom check failing on literal `version "..."` inside module plugins blocks. - **Upgrade in one place**: bump the `[versions]` ref; every module follows. ## Distinguish from dependency conflicts This is *not* the dependency resolution conflict you fix with `resolutionStrategy.force` or constraints — those apply to normal `dependencies {}`. Plugin requests are resolved earlier and must simply agree; the fix is de-duplication, not a conflict-resolution strategy.
- Why can't you fix a plugin version conflict with resolutionStrategy.force like a dependency conflict?Plugin requests are resolved at an earlier phase than the `dependencies {}` graph; Gradle requires the requested versions to agree rather than picking a winner. The fix is removing the duplicate declaration, not forcing one.
- Where should the single source of truth live?Typically the version catalog's [plugins] section, or `pluginManagement.plugins {}` in settings. Modules then apply by alias/id with no literal version.
- How would you prevent the conflict from recurring?Add a CI lint that fails when a module's plugins block contains a literal `version "..."`, forcing everyone to use the centralized catalog/pluginManagement.
saying these in an interview costs you the question
- Trying to resolve it with dependency resolutionStrategy.force.
- Adding the version in yet another place instead of de-duplicating.
- Assuming Gradle will silently pick the highest version (it won't for plugins).