Compose 1.12 forces AGP 9, and AGP 9 breaks kotlin-android — remove the plugin, don’t upgrade it

I bumped a Compose game’s dependency catalog to the current AndroidX train and expected the usual routine: new version numbers, one deprecation warning, done. Instead the build failed three different ways in a row, and the middle failure has a fix that is the opposite of what the error suggests.

TL;DR — Compose BOM 2026.08 (Compose 1.12) refuses anything below AGP 9.1; under AGP 9 the org.jetbrains.kotlin.android plugin crashes with a BaseExtension ClassCastException even at its latest version, because AGP 9 ships built-in Kotlin — delete the plugin instead of upgrading it; then raise compileSdk to 37.

The setup

A single-module Android game: Kotlin, Jetpack Compose, version catalog in gradle/libs.versions.toml, three plugins (com.android.application, org.jetbrains.kotlin.android, org.jetbrains.kotlin.plugin.compose). Starting point: AGP 8.10, Gradle 8.11, Kotlin 2.1, Compose BOM 2024.12, compileSdk 36. Target: Compose BOM 2026.08.00 and current AndroidX (core-ktx 1.19, lifecycle 2.11, appcompat 1.8).

What I expected

That AGP 8.13.2 — the last 8.x — would carry the new BOM. AAR metadata checks usually ask for a minimum AGP within the same major, and staying on 8.x avoids a major-version migration mid-task.

What actually happens

Wall one, with AGP 8.13.2 and Gradle 8.14.5:

> Could not resolve all files for configuration ':app:debugRuntimeClasspath'
> 26 issues were found when checking AAR metadata:
    1. Dependency 'androidx.compose.material:material-ripple-android:1.12.0'
       requires Android Gradle plugin 9.1.0 or higher.

So AGP 9 is not optional. Wall two, after moving to AGP 9.3.1 and Gradle 9.7.1, with the Kotlin plugin at its latest stable (2.3.21) and even with android.builtInKotlin=false set:

> Failed to apply plugin 'org.jetbrains.kotlin.android'.
> class com.android.build.gradle.internal.dsl.ApplicationExtensionImpl$AgpDecorated_Decorated
  cannot be cast to class com.android.build.gradle.BaseExtension

The reflex — and what a model answering from memory confidently told me — is « your Kotlin plugin is too old, upgrade it ». It is not. AGP 9 removed the legacy BaseExtension API that the standalone kotlin-android plugin casts to, and 2.3.21 still performs that cast. Wall three, once the plugin question is solved:

> 13 issues were found when checking AAR metadata:
    1. Dependency 'androidx.compose.material:material-ripple-android:1.12.0'
       requires ... to compile against version 37 or later of the Android APIs.

The three successive build failures and their fixes: AAR metadata demands AGP 9.1+, the kotlin-android plugin crashes on a BaseExtension cast and must be removed in favour of AGP 9 built-in Kotlin, then compileSdk must reach 37.

The fix

Remove the standalone Kotlin Android plugin and let AGP 9’s built-in Kotlin compile the module. Keep the Compose compiler plugin. Raise compileSdk.

// app/build.gradle.kts
plugins {
    alias(libs.plugins.android.application)
    // alias(libs.plugins.kotlin.android)   <- DELETE (root build.gradle.kts too)
    alias(libs.plugins.kotlin.compose)      // still needed for Compose
}

android {
    compileSdk = 37          // was 36; targetSdk can stay at 36
    kotlin {
        compilerOptions {    // this block now talks to AGP's built-in Kotlin
            jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
        }
    }
}
# gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip

Also delete any android.builtInKotlin=false from gradle.properties: it does not resurrect the old plugin, it just keeps you on the broken path. After these three changes the same project built, its 77 JVM unit tests passed, and the R8 release bundle installed and ran.

Why it works

AGP 9 bundles the Kotlin compiler and exposes a kotlin {} block inside the android {} extension, making the separate org.jetbrains.kotlin.android plugin redundant. The old plugin’s application path still downcasts the Android extension to BaseExtension, a type AGP 9 no longer implements — hence a ClassCastException at apply time, before any of your configuration runs, and at any plugin version available today. The Compose compiler plugin (org.jetbrains.kotlin.plugin.compose) is a different artifact that plugs into the compiler itself; it applies cleanly on top of built-in Kotlin. The compileSdk 37 demand is ordinary AAR metadata: Compose 1.12 and core 1.19 are compiled against API 37, and the SDK platform downloads automatically during the first build.

One cosmetic scare on the way: inspecting the release bundle showed android.permission.DUMP. That is the protection level on AndroidX ProfileInstaller’s receiver (android:permission=, who may send to it), not a <uses-permission> — the store listing still reports zero permissions.

What I did not test

AGP 9.0–9.2, Kotlin plugin 2.4.x, multi-module builds, KAPT/KSP (this project uses neither), and targetSdk 37. The kotlin { compilerOptions } placement inside android {} is what I had before the migration and it kept working; I did not try the top-level form under built-in Kotlin.

Related: Android never loads values-id — Indonesian resources must live in values-in.

Facts

context: single-module Kotlin/Compose Android app migrating its version catalog to the 2026 AndroidX train
problem: Compose BOM 2026.08 rejects AGP 8.x (AAR metadata), then kotlin-android 2.3.21 crashes on a BaseExtension ClassCastException under AGP 9, then compileSdk 36 is rejected
solution: AGP 9.3.1 + Gradle 9.7.1, delete org.jetbrains.kotlin.android (built-in Kotlin), keep org.jetbrains.kotlin.plugin.compose, compileSdk 37
verified_on: 2026-08-23
applies_to: [AGP 9.3.1, Gradle 9.7.1, Compose BOM 2026.08.00, Kotlin Gradle plugin <= 2.3.21, JDK 17]
does_not_apply_to: [AGP 8.x builds, projects using KAPT/KSP (untested), multi-module builds (untested)]

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Retour en haut