Question
Fix Java and Kotlin JVM Target Mismatch in Android Gradle Builds
Question
An Android Studio build fails during kaptGenerateStubsDebugKotlin with the following error:
'compileDebugJavaWithJavac' task (current target is 1.8) and
'kaptGenerateStubsDebugKotlin' task (current target is 17)
jvm target compatibility should be set to the same Java version.
How can the Java and Kotlin JVM target compatibility settings be configured so that Java compilation, Kotlin compilation, and KAPT use the same version?
Short Answer
You will learn what JVM target compatibility means in an Android Gradle build, why Java and Kotlin targets must match, and how to configure both consistently with compileOptions and a Kotlin JVM toolchain. You will also learn how KAPT relates to Kotlin compilation and how to diagnose conflicting Gradle settings.
Concept
Java and Kotlin source files are compiled into JVM bytecode. A JVM target specifies the bytecode version produced by a compiler.
In the error:
- Java compilation (
compileDebugJavaWithJavac) produces Java 8-compatible bytecode because its target is1.8. - Kotlin compilation, including the KAPT stub-generation task (
kaptGenerateStubsDebugKotlin), is configured to target Java 17 bytecode.
These tasks contribute classes to the same Android app variant, debug. Gradle rejects the build because mixing target bytecode versions can cause inconsistent compilation and tooling behavior.
The important rule is:
Configure Java and Kotlin to target the same JVM version for every Android module that contains both languages.
KAPT is Kotlin's annotation-processing tool. Before Java annotation processors such as Room, Dagger, or Hilt can run, KAPT generates Java-like stubs from Kotlin code. Therefore, a KAPT error often reveals a Kotlin compiler configuration issue rather than a problem in an annotation processor.
Mental Model
Think of Java and Kotlin compilation as two machines making parts for the same product.
- The Java machine is set to the Java 8 specification.
- The Kotlin machine is set to the Java 17 specification.
Although both machines produce bytecode, their parts follow different versions of the specification. Gradle stops the assembly line rather than producing an app with mismatched parts.
KAPT is an extra station attached to the Kotlin machine. If Kotlin is configured for Java 17, KAPT stubs are also associated with that Kotlin target. Fix the main Kotlin and Java settings, and the KAPT task will use the consistent configuration.
Syntax and Examples
For a modern Android project using Kotlin DSL (build.gradle.kts), choose one JVM version and configure both Java and Kotlin to use it.
Target Java 17
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
jvmToolchain(17)
}
This is a common configuration for modern Android projects whose Android Gradle Plugin and Gradle versions support JDK 17.
sourceCompatibilitycontrols Java language features accepted by the Java compiler.targetCompatibilitycontrols the Java bytecode target.jvmToolchain(17)selects a JDK/toolchain for Kotlin compilation and aligns Kotlin's target with Java 17.
Target Java 8 instead
If the project must remain on Java 8 bytecode, use the same version everywhere:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
kotlin {
jvmToolchain(8)
}
Do not configure Java for 8 while configuring Kotlin for 17, or the reverse.
Groovy DSL equivalent
For build.gradle rather than build.gradle.kts:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}
kotlin {
jvmToolchain(17)
}
Use only the syntax matching the Gradle file in your project.
Step by Step Execution
Consider this mismatched configuration:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
kotlin {
jvmToolchain(17)
}
During a debug build, Gradle performs these steps:
- The Android Gradle Plugin configures
compileDebugJavaWithJavacfromcompileOptions. - That Java task receives target compatibility
1.8. - The Kotlin plugin configures Kotlin compile-related tasks, including
kaptGenerateStubsDebugKotlin, from the Kotlin toolchain. - Those Kotlin tasks receive a target of
17. - Kotlin Gradle plugin validation compares the Java target and Kotlin target for the same variant.
- It finds
1.8versus17and fails before annotation processing proceeds.
Fix it by making the settings agree:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
jvmToolchain(17)
}
Now both sides target 17, so KAPT can generate stubs and the build can continue.
Real World Use Cases
Matching JVM targets matters whenever an Android module contains Java and Kotlin, including:
- Android applications: Kotlin Activities and Java utility classes are compiled into the same app variant.
- Room databases: KAPT or KSP generates database code from Kotlin entities and DAOs.
- Dagger or Hilt: Annotation processors generate dependency-injection code that must compile with the rest of the module.
- Multi-module apps: Each Android library module needs internally consistent Java and Kotlin settings.
- Shared build conventions: A convention plugin can apply one standard JVM target to all modules, preventing configuration drift.
The target version is separate from Android's minSdk. Android API compatibility is controlled by SDK settings and, when required, desugaring. JVM target compatibility controls the compiler bytecode level used by Java and Kotlin tasks.
Real Codebase Usage
In production projects, developers usually centralize this configuration rather than setting unrelated values in each task.
Preferred pattern: one Java target and one Kotlin toolchain
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
jvmToolchain(17)
}
A JVM toolchain is preferable to relying only on whichever JDK happens to run Gradle. It makes builds more reproducible across developer machines and CI.
Check all Android modules
A multi-module project may have app, feature-*, and core-* modules. Configure each module that applies Android and Kotlin plugins, or place the shared rules in a convention plugin.
Use KAPT only when needed
KAPT does not need a special target setting of its own in the normal case. It follows Kotlin compilation configuration. Configure the Java and Kotlin compilers correctly rather than attempting to fix only kaptGenerateStubsDebugKotlin.
Legacy Kotlin configuration
Older projects may explicitly configure kotlinOptions.jvmTarget. If it exists, it must agree with Java:
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.jvmTarget = "17"
}
For newer Kotlin Gradle plugin setups, prefer kotlin { jvmToolchain(17) } instead of adding task-by-task configuration.
Common Mistakes
Changing only sourceCompatibility
This does not fully describe the bytecode target. Set both Java properties:
// Incomplete configuration
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
}
Use:
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
Configuring Java but not Kotlin
android {
compileOptions {
targetCompatibility = JavaVersion.VERSION_17
}
}
// Kotlin may still have an explicit jvmTarget of "1.8" elsewhere.
Always inspect Kotlin configuration, including jvmToolchain(...) and any older kotlinOptions.jvmTarget setting.
Keeping contradictory old and new settings
A project may contain a legacy kotlinOptions.jvmTarget = "1.8" plus a new toolchain of 17. Remove or update stale settings so one intended version is clear.
Editing the wrong Gradle file
The failing task begins with :app:, so start with the app module's Gradle file, usually app/build.gradle.kts or app/build.gradle. Also check applied convention plugins or root scripts that can override module settings.
Assuming the installed JDK alone fixes targets
Running Gradle with JDK 17 does not automatically mean Java source is compiled with target 17. compileOptions can still explicitly target Java 8. The JDK used to run Gradle and the target bytecode version are related but distinct settings.
Using Java 17 language APIs without Android compatibility planning
A Java 17 target does not automatically make every Java 17 standard-library API available on every Android device. When using newer Java APIs on older Android versions, verify Android support and configure core library desugaring when appropriate.
Comparisons
| Setting or term | What it controls | Example |
|---|---|---|
sourceCompatibility | Java language level accepted by javac | Whether Java 17 syntax can be used |
targetCompatibility | Bytecode target produced by Java compilation | JavaVersion.VERSION_17 |
Kotlin jvmToolchain(17) | JDK/toolchain Kotlin uses and its JVM target configuration | Kotlin compilation aligned to 17 |
kotlinOptions.jvmTarget | Legacy task-level Kotlin bytecode target setting | "17" |
| Gradle runtime JDK | JDK used to execute Gradle itself | Configured in Android Studio or org.gradle.java.home |
minSdk | Lowest Android API level supported by the app | minSdk = 24 |
Java 8 versus Java 17 target
| Choice | When it is useful | Important consideration |
|---|---|---|
| Java 8 | Legacy projects or builds constrained by older tooling | Configure Java and Kotlin consistently to 8 |
| Java 17 | Modern Android projects using compatible Gradle and Android Gradle Plugin versions | Ensure all modules and plugins support the selected toolchain |
The correct choice is not always the highest available version. It is the version that your project tooling supports and that is configured consistently across the build.
Cheat Sheet
- A Java/Kotlin Android module needs matching JVM targets.
- The error
1.8versus17means Java and Kotlin compilation settings disagree. compileDebugJavaWithJavacgets its target from AndroidcompileOptions.kaptGenerateStubsDebugKotlinfollows Kotlin compilation settings.- Modern Kotlin DSL configuration for Java 17:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
}
kotlin {
jvmToolchain(17)
}
- For Java 8, use
VERSION_1_8andjvmToolchain(8). - Check for stale
kotlinOptions.jvmTargetsettings that override or contradict the intended target. - Apply consistent settings to every module that compiles Java and Kotlin.
- The JDK that runs Gradle is not necessarily the Java bytecode target.
FAQ
Why does the error mention KAPT rather than my Kotlin file?
KAPT generates Java stubs from Kotlin source before annotation processors run. It is a Kotlin compile-related task, so it detects the Kotlin-versus-Java target mismatch.
Do I need to configure KAPT separately?
Usually no. KAPT uses the Kotlin compiler configuration. Align android.compileOptions with the Kotlin toolchain or Kotlin JVM target.
Should I use Java 8 or Java 17 for an Android project?
Use a version supported by your Android Gradle Plugin, Gradle version, dependencies, and team environment. Either can work when Java and Kotlin are configured to the same target.
Is jvmToolchain(17) the same as setting Android minSdk to 17?
No. jvmToolchain(17) refers to Java version 17. minSdk refers to Android API level 17. They are different version systems.
Why does Android Studio use JDK 17 but Java compilation still targets 1.8?
The JDK running Android Studio or Gradle is separate from targetCompatibility. Your Gradle configuration can explicitly compile Java bytecode for 1.8 even when Gradle runs on JDK 17.
Where should I look for a conflicting JVM target?
Check the failing module first, such as app/build.gradle.kts. Then search the project for jvmTarget, jvmToolchain, sourceCompatibility, and targetCompatibility, including convention plugins.
Does changing the JVM target require a clean build?
It is often helpful to run a clean build after changing compiler configuration, but it does not replace correcting the conflicting settings.
Mini Project
Description
Configure a small Android module that contains Kotlin and Java code, plus KAPT. The project demonstrates how one shared JVM target prevents compiler and annotation-processing tasks from disagreeing.
Goal
Make the debug build use Java 17 consistently for Java compilation, Kotlin compilation, and KAPT stub generation.
Requirements
Configure Java source and target compatibility as Java 17. Configure Kotlin to use a Java 17 JVM toolchain. Enable KAPT in the module. Include one Kotlin class and one Java class in the same module. Verify that no older Kotlin JVM target setting conflicts with Java 17.
Keep learning
Related questions
Accessing Kotlin Extension Functions from Java
Learn how Kotlin extension functions are compiled and how to call them correctly from Java with clear examples and common pitfalls.
Add a Maven Repository URL in Gradle Kotlin DSL (build.gradle.kts)
Learn how to add Maven Central and a custom Maven repository URL in Gradle Kotlin DSL using build.gradle.kts syntax.
Allow HTTP and HTTPS in Android 9 Pie with Network Security Configuration
Learn how Android 9 Pie handles cleartext HTTP traffic and how to allow HTTP and HTTPS safely using network security config.