Dependency of lower version is selected by default

Hello,

Please advise why does gradle 7.5 chooses lower version of a dependency which is declared in ‘api’ configuration with a higher version?

We have this in one of the libraries:
api("io.kubernetes:client-java:18.0.0")

When that library is used in another project, gradle somehow resolves that transitive ‘client-java’ dependency to 13.0.2:

./gradlew dependencyInsight --dependency io.kubernetes:client-java --configuration cucumber
Skipping Maven repo publishing setup for project <root> because no extension property 'dkType' is set for it

> Task :dependencyInsight
io.kubernetes:client-java:13.0.2 (selected by rule)
  Variant runtime:
    | Attribute Name             | Provided     | Requested |
    |----------------------------|--------------|-----------|
    | org.gradle.category        | library      |           |
    | org.gradle.libraryelements | jar          |           |
    | org.gradle.status          | release      |           |
    | org.gradle.usage           | java-runtime |           |

io.kubernetes:client-java:18.0.0 -> 13.0.2
\--- com.jago.common:k8s-test-environment:1.14.36
     +--- com.jago.common:k8s-test-redis-environment:1.14.36
     |    \--- cucumber
     +--- com.jago.common:k8s-test-kafka-environment:1.14.36
     |    \--- com.jago.common:k8s-kafka-cucumber:1.14.36
     |         \--- cucumber
     \--- com.jago.common:k8s-test-mongo-environment:1.14.36
          \--- com.jago.common:k8s-mongo-cucumber:1.14.36
               \--- cucumber

io.kubernetes:client-java-api:13.0.2
  Variant runtime:
    | Attribute Name             | Provided     | Requested |
    |----------------------------|--------------|-----------|
    | org.gradle.category        | library      |           |
    | org.gradle.libraryelements | jar          |           |
    | org.gradle.status          | release      |           |
    | org.gradle.usage           | java-runtime |           |

io.kubernetes:client-java-api:13.0.2
\--- io.kubernetes:client-java:13.0.2
     \--- com.jago.common:k8s-test-environment:1.14.36 (requested io.kubernetes:client-java:18.0.0)
          +--- com.jago.common:k8s-test-redis-environment:1.14.36
          |    \--- cucumber
          +--- com.jago.common:k8s-test-kafka-environment:1.14.36
          |    \--- com.jago.common:k8s-kafka-cucumber:1.14.36
          |         \--- cucumber
          \--- com.jago.common:k8s-test-mongo-environment:1.14.36
               \--- com.jago.common:k8s-mongo-cucumber:1.14.36
                    \--- cucumber

io.kubernetes:client-java-proto:13.0.2
  Variant runtime:
    | Attribute Name             | Provided     | Requested |
    |----------------------------|--------------|-----------|
    | org.gradle.category        | library      |           |
    | org.gradle.libraryelements | jar          |           |
    | org.gradle.status          | release      |           |
    | org.gradle.usage           | java-runtime |           |

io.kubernetes:client-java-proto:13.0.2
\--- io.kubernetes:client-java:13.0.2
     \--- com.jago.common:k8s-test-environment:1.14.36 (requested io.kubernetes:client-java:18.0.0)
          +--- com.jago.common:k8s-test-redis-environment:1.14.36
          |    \--- cucumber
          +--- com.jago.common:k8s-test-kafka-environment:1.14.36
          |    \--- com.jago.common:k8s-kafka-cucumber:1.14.36
          |         \--- cucumber
          \--- com.jago.common:k8s-test-mongo-environment:1.14.36
               \--- com.jago.common:k8s-mongo-cucumber:1.14.36
                    \--- cucumber

(*) - dependencies omitted (listed previously)

A web-based, searchable dependency report is available by adding the --scan option.

Deprecated Gradle features were used in this build, making it incompatible with Gradle 8.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

See https://docs.gradle.org/7.5/userguide/command_line_interface.html#sec:command_line_warnings
  • we don’t use any custom dependency resolution strategy in the project
  • we declare that io.kubernetes:client-java dependency in a single place and it has version 18.0.0 there (as mentioned above)

The problem was workarounded by explicitly declaring not only ‘client-java’ but ‘client-java-api’ and ‘client-java-proto’ in ‘api’ configuration like below:

    val k8sDependencyVersion = "18.0.0"
    api("io.kubernetes:client-java:$k8sDependencyVersion")
    api("io.kubernetes:client-java-api:$k8sDependencyVersion")
    api("io.kubernetes:client-java-proto:$k8sDependencyVersion")

Please advise on the unexpected initial version resolution - what is the reason for that?

Can you show a build --scan? That usually should make it pretty clear where it comes from.

Do you maybe use the obsolete Spring dependency management plugin? Because that also often causes confusing version resolution with hard to see reason.

Thank you for the help!

build --scan - Build Scan® | Gradle Cloud Services

We do use spring dependency management plugin 1.0.12.RELEASE, it’s rather old indeed - the times of spring boot 2.7.2

Yeah, looks like enforced by the spring dependency management plugin as I expected. So you probably have that version in a BOM you import. Replace the plugin by the built-in BOM support.

thanks! Could you share how did you see that in the build scan output? I saw only numerous ‘detachedConfigurations’

Iirc, the lots of detached configurations come from the Kotlin plugin.

That the version comes from the spring dependency management plugin is an educated guess from experience.
You see that the plugin is applied, you see that the version is “Selected by rule” and does not have any other origin like a strict version or enforced constraint.
This “rule” could of course also be in your build scripts somewhere. But chances are that you would have found it then.
So the combination of these things is a strong pointer towards the spring dependency management plugin.

thanks a lot for the analysis and explanation!