Because Gradle resolves a version conflict by selecting the highest version that any part of your dependency graph asks for. If another library needs a newer release of an SDK than the one you declared, you get the newer one, even though your build file never changed.
What is a version conflict?
A version conflict happens when two components depend on the same module at different versions. Your app might declare analytics SDK 4.1, while a payment library you also use depends on analytics SDK 4.3. Gradle builds one resolved graph, so only one version can ship in the app.
According to the Gradle documentation, the default behaviour is to pick the highest version. The details depend on how versions are declared. Without ranges, the highest non-rejected version wins. With a strictly declaration that is lower than the highest requested version, resolution fails rather than quietly downgrading.
Why this matters for upgrade and deprecation risk
Two things follow for mobile teams.
First, an SDK can upgrade without anyone touching it. A routine bump of a neighbouring library can pull your SDK to a version with new behaviour, a removed method or a changed permission. The release notes you read were for the version you thought you had.
Second, the reverse is also true. You may believe you fixed a vulnerable SDK by bumping it, yet another dependency with a strict constraint holds an older copy in place, or resolution fails and nobody notices until the build breaks.
How to see what you actually ship
- Run the dependency report for your release configuration, for example the runtime classpath of the release variant, and search it for the SDK name.
- Look for lines showing one version arrowed to another. That marks a conflict where Gradle overrode a requested version.
- Note who asked for the higher version. The path to the dependency tells you which library pulled it.
- Record the resolved version in the release notes, not the declared one.
The same report is the right place to check variants. A debug build can resolve differently from release if dependencies are declared per configuration.
Ways to control it
| Approach | What it does | Trade off |
|---|---|---|
| Do nothing | Highest version wins | Surprise upgrades from neighbours |
| Declare the version you want and keep it highest | Your choice wins only if it is the highest | Does not stop a later neighbour from going higher |
| Use a strict constraint | Fails if a higher version is requested | Forces a decision, can block builds |
| Use a dependency lock | Freezes resolved versions between runs | You must refresh it on purpose |
| Use a platform or bill of materials | Moves related modules together | Needs the vendor to publish one |
Locking is covered in our post on locking Android SDK versions with Gradle lockfiles. A lock records what resolved. A strict constraint states what you will accept. They solve different halves of the problem.
Try it yourself
Click through it at your own pace, or open it full screen.
Look for the difference between a direct dependency and one that arrives through another library. The second kind is where silent upgrades come from.
Where Safeguard fits
Dependency scanning and SBOM generation in Safeguard list the resolved versions in a build, including transitive ones, so you can compare them against what you declared. You can try a free scan at gold.safeguard.sh/scan.
Quick check
Does a higher version always win?
By default, yes, for versions without ranges. A strictly declared version that is lower than the highest requested one makes resolution fail instead.
Can my SDK change when I did not edit my build file?
Yes. If a different dependency requests a newer version of the same SDK, Gradle selects it. Check the resolved graph, not the declaration.
Does a lockfile stop this?
A lock keeps the resolved set stable between builds. It does not decide which version is right, so review the lock diff whenever it changes.
Sources
- Gradle documentation, Graph Resolution and Conflict Resolution: https://docs.gradle.org/current/userguide/graph_resolution.html
Last reviewed: 19 October 2026.