Summary
A failed release build occurred due to unresolved dependency org.jetbrains.kotlin:kotlin-stdlib-common:2.2.0, which was locked in myLibrary‘s Gradle lockfile. The error surfaced during R8 minification when the locked version conflicted with transitive dependency resolution in a multi-module Android project.
Root Cause
The failure resulted from:
- Dependency locking applying to Android runtime configurations, which rely on implicit Kotlin dependencies.
- Lockfiles capturing
kotlin-stdlib-common:2.2.0during resolution, but Android projects implicitly use JDK variants (e.g.,kotlin-stdlib-jdk8). - R8 resolution requiring dependencies absent in the lockfile during the minification task (
minifyLibDefaultReleaseWithR8).
Why This Happens in Real Systems
- Android Gradle Plugin (AGP) uses derived configurations (e.g.,
*RuntimeClasspath) incompatible with strict locking. - Transitive Kotlin dependencies are implicitly injected by Kotlin plugin but locked unintentionally.
- Multi-project builds compound resolution mismatches when locked dependencies conflict across modules.
Real-World Impact
If unaddressed:
- Release builds fail unpredictably despite successful debug builds.
- Dependency version drift occurs when lockfiles override Android’s dependency substitutions.
- Critical toolchain libraries (Kotlin stdlib) become unresolvable, halting deployments.
Example or Code
Selective locking setup (applied globally in root build.gradle):
dependencyLocking {
lockAllConfigurations()
lockMode = LockMode.STRICT
// Explicitly skip Android runtime configurations
unlockAllConfigurations()
lockConfigurations = [
"api",
"implementation",
"compileClasspath",
"annotationProcessor",
"testImplementation"
]
}
How Senior Engineers Fix It
- Avoid locking Kotlin stdlib:
- Explicitly exclude Kotlin artifacts from lockfiles:
configurations.all { resolutionStrategy { componentSelection { all { ComponentSelection selection -> if (selection.candidate.group == 'org.jetbrains.kotlin' && selection.candidate.module.contains('stdlib')) { selection.reject("Excluding Kotlin stdlib from locking") } } } } }
- Explicitly exclude Kotlin artifacts from lockfiles:
- Restrict locking to explicit configurations:
- Lock only compile/declaration configurations (
api,implementation) and unlock implicit or runtime configurations.
- Lock only compile/declaration configurations (
- Use dependency constraints to implicitly manage Kotlin versions:
dependencies { constraints { implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0") implementation("org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0") } } - Centralize dependency versions using Gradle version catalogs to avoid conflicts.
Why Juniors Miss It
- Unfamiliarity with Android’s complex configuration hierarchy, particularly runtimeClasspath derivations.
- Overlooking implicit Kotlin dependencies injected by plugins.
- Misapplying generic locking patterns to dynamic Android projects without platform-specific filters.
- Debug vs. release configuration discrepancies hiding locking oversights until minification tasks execute.
- Assuming locking applies uniformly without considering AGP’s dependency resolution quirks.