fix(android): skip explicit Kotlin plugin when AGP registers the kotlin extension - #446
Open
gabrieldonadel wants to merge 2 commits into
Open
gabrieldonadel wants to merge 2 commits into
gabrieldonadel wants to merge 2 commits into
Conversation
…Kotlin AGP 9 enables built-in Kotlin by default and applies the Kotlin plugin itself. Applying it again fails configuration with "Cannot add extension with name 'kotlin'". Guard the explicit apply so it only runs when AGP is not providing Kotlin: AGP 8 and older, or AGP 9 with android.builtInKotlin=false. AGP 10 removes that opt-out, so built-in Kotlin is always active there and the explicit apply must never run.
gabrieldonadel
force-pushed
the
fix/agp9-built-in-kotlin
branch
from
September 2, 2026 21:03
835b7d2 to
1c97e44
Compare
Replace the AGP version / android.builtInKotlin check with a direct test for the registered kotlin extension. The version check reads com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION, which can resolve to a different classpath entry than the AGP actually in use, and the global android.builtInKotlin property can be overridden per module by the com.android.built-in-kotlin plugin -- so both inputs can disagree with reality. Asking whether the kotlin extension exists tests the condition that actually fails, needs no AGP version table, and covers AGP 10 where the opt-out is removed. Co-authored-by: gabrieldonadel <11707729+gabrieldonadel@users.noreply.github.com>
Author
|
Pushed a revision that changes how the guard decides, not what it does. Before, it derived the answer from the AGP version and the if (project.extensions.findByName('kotlin') == null) {
apply plugin: 'kotlin-android'
}Three reasons this is the better shape:
This shape was suggested by the RevenueCat maintainers on |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Android Gradle Plugin 9 ships built-in Kotlin support and enables it by default, so
AGP registers the
kotlinextension itself. When a library also applieskotlin-androidexplicitly, the two collide and configuration fails before anything compiles. AGP
words it two ways, both the same problem:
The apply is unconditional in this file, so on an AGP 9 project it cannot be built
at all. There is no consumer-side workaround short of patching the file — setting
android.builtInKotlin=falseproject-wide just to build one dependency is not areasonable ask, and that escape hatch is removed in AGP 10.
Change
Apply the plugin only when nothing has registered the
kotlinextension yet:Files changed:
android/build.gradleWhy this shape
Earlier revisions of this PR derived the answer from the AGP version and the
android.builtInKotlinproperty. Asking for the extension directly is better onthree counts:
registered
kotlin", so that is what the guard checks. No AGP version table to keepin sync.
android.builtInKotlinis a global switch, but built-inKotlin can also be enabled per module with the
com.android.built-in-kotlinplugin,so the global value can disagree with the module. Reading
com.android.Version.ANDROID_GRADLE_PLUGIN_VERSIONhas its own trap: it resolvesagainst the buildscript classpath, which is not always the AGP that ends up running.
android.builtInKotlinopt-out is removed there,and this guard needs no special case for it.
android.builtInKotlinkotlinextensionfalsetruefalseThe guard sits after
apply plugin: 'com.android.library'in every file it touches,so AGP has already registered its extensions by the time it runs. I checked that
ordering per file rather than assuming it.
What I verified, and what I did not
9.2.1 and Gradle 9.4.1:
:app:assembleDebugsucceeds both with-Pandroid.newDsl=true -Pandroid.builtInKotlin=trueand with both flags off. Thatrun used the earlier version-based guard; the extension guard in this revision is
the shape now used across the rest of this sweep and already merged in several of
those repos.
Phases.CONVERSION.and I could not do that from outside.
Found while sweeping 157 popular React Native libraries for AGP 9 new-DSL compatibility.
34 failed with the new DSL enabled, and 29 of those failed on exactly this — it is the
most common blocker by a wide margin.
This guard shape was suggested by the RevenueCat maintainers on
RevenueCat/react-native-purchases#1934,
who had already hit the same issue in their Capacitor and Flutter SDKs. I have
since standardised on it across this sweep.