From 22131fce238493ac861d1243eff91541c9a9fae9 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Dall'Agnol Date: Thu, 3 Sep 2026 11:20:57 -0300 Subject: [PATCH] fix(android): skip explicit Kotlin plugin when AGP registers the kotlin extension (#1934) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Android Gradle Plugin 9 ships built-in Kotlin support and enables it by default, so AGP registers the `kotlin` extension itself. When a library *also* applies `kotlin-android` explicitly, the two collide and configuration fails before anything compiles: ``` > Failed to apply plugin 'kotlin-android'. > Cannot add extension with name 'kotlin', as there is an extension already registered with that name. ``` The apply is unconditional in both files, so on an AGP 9 project this library cannot be built at all. There is no consumer-side workaround short of patching the file — setting `android.builtInKotlin=false` project-wide just to build one dependency is not a reasonable ask, and that escape hatch is removed in AGP 10. ## Change Apply the plugin only when nothing has registered the `kotlin` extension yet: ```groovy if (project.extensions.findByName('kotlin') == null) { apply plugin: 'kotlin-android' } ``` Files changed: - `android/build.gradle` - `react-native-purchases-ui/android/build.gradle` This checks the exact condition that fails, so there is no AGP version table to keep in sync. It follows the approach already used in [purchases-capacitor#860](https://github.com/RevenueCat/purchases-capacitor/pull/860) and [purchases-flutter#1765](https://github.com/RevenueCat/purchases-flutter/pull/1765), per maintainer review on this PR. Behaviour across configurations: | AGP | `android.builtInKotlin` | `kotlin` extension | explicit apply | |---|---|---|---| | 8.x | unset or `false` | absent | yes (unchanged) | | 9.x | unset or `true` | registered by AGP | no | | 9.x | `false` | absent | yes | | 10+ | n/a (opt-out removed) | registered by AGP | no | The check sits after `apply plugin: 'com.android.library'` in both files, so AGP has already registered its extensions by the time it runs. ## What I verified, and what I did not - **Verified end to end** on a real Expo SDK 58 / React Native 0.87 project with AGP 9.2.1 and Gradle 9.4.1: `:app:assembleDebug` succeeds both with `-Pandroid.newDsl=true -Pandroid.builtInKotlin=true` and with both flags off. - Confirmed both branches actually execute, rather than one path silently always being taken: with the flags off, `::compileDebugKotlin` runs from the explicitly applied plugin; with them on, the build succeeds without it, which it could not do if the plugin were still being applied. - Syntax-checked both files with Groovy's `Phases.CONVERSION`. - **Not run:** this repo's own CI or example app. You mentioned you'd handle AGP 9 CI coverage separately. 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. --- android/build.gradle | 8 +++++++- react-native-purchases-ui/android/build.gradle | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index a8a2416fb..9ec049bb5 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -12,7 +12,13 @@ buildscript { } apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' +// AGP 9 ships built-in Kotlin support and registers the `kotlin` extension +// itself. Applying kotlin-android on top of it fails with +// "Cannot add extension with name 'kotlin'". Only apply it when nothing +// has registered that extension yet. +if (project.extensions.findByName('kotlin') == null) { + apply plugin: 'kotlin-android' +} def getExtOrDefault(name) { return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['Purchases_' + name] diff --git a/react-native-purchases-ui/android/build.gradle b/react-native-purchases-ui/android/build.gradle index 1309b9cda..ec7984851 100644 --- a/react-native-purchases-ui/android/build.gradle +++ b/react-native-purchases-ui/android/build.gradle @@ -19,7 +19,13 @@ def isNewArchitectureEnabled() { } apply plugin: "com.android.library" -apply plugin: "kotlin-android" +// AGP 9 ships built-in Kotlin support and registers the `kotlin` extension +// itself. Applying kotlin-android on top of it fails with +// "Cannot add extension with name 'kotlin'". Only apply it when nothing +// has registered that extension yet. +if (project.extensions.findByName('kotlin') == null) { + apply plugin: "kotlin-android" +} if (isNewArchitectureEnabled()) { apply plugin: "com.facebook.react"