Upgrading to AGP 9: the Kotlin plugin you can delete
Since Android Gradle plugin 9.0, Kotlin is built into AGP. Here is what to remove, what to move, why kapt has to go, and the one icons dependency Material 3 stopped adding for you.

You open an older Android project, bump the Android Gradle plugin to 9, and sync. The build stops with one of two messages: "Cannot add extension with name 'kotlin'" or "The 'org.jetbrains.kotlin.android' plugin is no longer required". Nothing in your code changed. The build files did not keep up with AGP.
This post covers what changed in AGP 9, the small set of edits that fix it, and two related surprises: kapt and the Material icons. Everything here comes from Chapter 2 (Thinking in Composables) and Chapter 8 (Saving Data: Room and DataStore) of Compose Without the Confusion.
The trap: applying Kotlin twice
Since Android Gradle plugin 9.0, Kotlin support is built into AGP itself. You no longer apply the separate org.jetbrains.kotlin.android plugin. Older projects apply it anyway, because that was the rule for years. With AGP 9 the plugin and AGP both try to set up Kotlin, and the sync fails with the messages above.
There is a second leftover in most older modules: an android { kotlinOptions { } } block. That block belonged to the old plugin, so it has to move too.
The fix: three edits
Google documents the migration for built-in Kotlin. In short:
- Remove the kotlin-android plugin from your build files.
- Remove it from your version catalog as well, so nothing re-adds it.
- Move any
android { kotlinOptions { } }block tokotlin { compilerOptions { } }.
One plugin stays. You still apply the Compose compiler plugin. It ships with Kotlin and uses the same version number, so it points at the kotlin version in your catalog.
What a working AGP 9 setup looks like
Here is the version catalog from the book's sample project, using the versions current at the time of writing (October 2026):
# gradle/libs.versions.toml (excerpt)
[versions]
agp = "9.4.0"
kotlin = "2.4.21"
ksp = "2.3.12"
composeBom = "2026.09.00"
activity = "1.13.0"
lifecycle = "2.11.0"
[libraries]
compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
compose-ui = { module = "androidx.compose.ui:ui" }
compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" }
compose-material3 = { module = "androidx.compose.material3:material3" }
compose-icons-core = { module = "androidx.compose.material:material-icons-core" }
[libraries.activity-compose]
module = "androidx.activity:activity-compose"
version.ref = "activity"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
Notice what is missing from [plugins]: there is no kotlin-android entry. The only Kotlin-related plugin is kotlin-compose, and its version is the Kotlin version.
The module build file is just as short:
// app/build.gradle.kts (excerpt)
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "com.example.pantry"
compileSdk = 36 // use the newest stable API level your AGP supports
defaultConfig {
applicationId = "com.example.pantry"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
}
buildFeatures { compose = true }
}
Two plugins. No kotlinOptions inside android { }. If your module still has compiler flags, they go in a top-level kotlin { compilerOptions { } } block instead.
The second trap: kapt
If your project uses Room, Hilt or another annotation processor through kapt, the plugin cleanup is not the end. With AGP 9's built-in Kotlin, the old kotlin-kapt plugin is not compatible. Google's migration guide recommends moving to KSP, and offers com.android.legacy-kapt only as a stopgap. Room, Hilt and most annotation processors support KSP today.
Moving Room to KSP is a few lines. The catalog gets the KSP and Room plugins (the ksp version is already in [versions] above):
# gradle/libs.versions.toml (additions)
[versions]
room = "2.8.5"
[libraries]
room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
[plugins]
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
room = { id = "androidx.room", version.ref = "room" }
And the module applies them, swapping kapt(...) for ksp(...) on the compiler:
// app/build.gradle.kts
plugins {
// ...
alias(libs.plugins.ksp)
alias(libs.plugins.room)
}
room {
schemaDirectory("$projectDir/schemas")
}
dependencies {
implementation(libs.room.runtime)
implementation(libs.room.ktx)
ksp(libs.room.compiler)
}
The Room Gradle plugin's schemaDirectory writes a JSON description of every database version into your project. Commit those files. Room uses them for automatic migrations and for migration tests.
Hilt works the same way. It is built on Dagger and processed with KSP. At the time of writing the Dagger and Hilt release is 2.60.1.
The third trap: icons that stop resolving
This one is not AGP, but it tends to arrive in the same upgrade. Since Material 3 1.4.0, the library no longer pulls in material-icons-core for you. Its release notes say so. After the upgrade, Icons.Default.Add suddenly does not resolve, and the error looks unrelated to anything you touched.
The fix is to add the artifact yourself. It is already in the catalog above as compose-icons-core, and the BOM still manages its version:
dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui)
implementation(libs.compose.material3)
implementation(libs.compose.icons.core) // Icons.Default.*
implementation(libs.compose.ui.tooling.preview)
implementation(libs.activity.compose)
debugImplementation(libs.compose.ui.tooling)
}
The BOM line is the one that saves you the most pain. You declare one BOM version and leave the version off every individual Compose library. The BOM picks a set of versions that were tested together.
Checklist for an AGP 9 upgrade
- Remove
org.jetbrains.kotlin.androidfrom every module and from the version catalog. - Move
android { kotlinOptions { } }tokotlin { compilerOptions { } }. - Keep the Compose compiler plugin (
org.jetbrains.kotlin.plugin.compose) at your Kotlin version. - Replace kapt with KSP. Use
com.android.legacy-kaptonly as a stopgap. - For Room, add the Room Gradle plugin, set
schemaDirectory, and commit the schema files. - If
Icons.Defaultstops resolving, addmaterial-icons-coreand let the BOM version it. - Leave versions off individual Compose libraries and let one BOM version pick them.

