Resolving Gradle Configuration Errors in Multi-Module Android Apps
Learn how to fix Gradle configuration errors in multi-module Android applications using Kotlin DSL and version catalogs.
How to resolve Gradle configuration errors in a multi-module Android application? I’m experiencing two main issues:
- When using the Kotlin DSL in my build.gradle file with the plugins block:
plugins {
'kotlin-dsl'
}
Android Studio shows the error: ‘There are too many characters in the character literal.’ However, using double quotes makes the expression turn gray, and I’m unsure if it’s being interpreted correctly.
- In my build.gradle file, I’m trying to reference dependencies defined in my build.settings file:
dependencies {
implementation(libs.plugin.android.application)
implementation(libs.plugin.kotlin.android)
implementation(libs.plugin.kotlin.compose)
}
But Android Studio marks ‘android’ and ‘compose’ as unresolved references with the error: ‘Unresolved reference.’
My build.settings file contains:
[libraries]
plugin-android-application = { module = "com.android.application:com.android.application.gradle.plugin", version.ref = "agp" }
plugin-kotlin-android = { module = "org.jetbrains.kotlin.android:org.jetbrains.kotlin.android.gradle.plugin", version.ref = "kotlin" }
plugin-kotlin-compose = { module = "org.jetbrains.kotlin.plugin.compose:org.jetbrains.kotlin.plugin.compose.gradle.plugin", version.ref = "kotlin" }
What causes these errors and how can I properly configure my multi-module Android application using Kotlin DSL and version catalogs?
Gradle configuration errors in multi-module Android applications commonly stem from Kotlin DSL syntax issues and incorrect version catalog setup. These problems can be resolved by using proper quote syntax in the plugins block and correctly configuring your version catalogs with the right file structure and dependency references.
Contents
Understanding Kotlin DSL Syntax Requirements
The first issue you’re encountering with the plugins block is a fundamental Kotlin DSL syntax requirement. Unlike Groovy, Kotlin DSL has stricter syntax rules that must be followed.
The Quote Syntax Problem
Your current code uses single quotes:
plugins {
'kotlin-dsl'
}
This causes the error “There are too many characters in the character literal” because Kotlin DSL requires double quotes for string literals. The correct syntax should be:
plugins {
"kotlin-dsl"
}
However, there’s more to plugin configuration than just the quotes. In modern Android projects using Kotlin DSL, plugins should be declared in a specific way that integrates with version catalogs.
Plugin Declaration Best Practices
According to the official Android documentation, plugins should be declared using the proper syntax in the plugins block. Here’s how to correctly define plugins:
plugins {
id("com.android.application")
kotlin("android")
kotlin("compose")
}
For multi-module projects, you might want to apply plugins conditionally:
plugins {
id("com.android.application") apply false
kotlin("android") apply false
kotlin("compose") apply false
}
The apply false syntax adds the plugin to the build’s classpath without applying it to the current project, which is particularly useful in multi-module setups where you want to control which modules receive which plugins.
Why Double Quotes Turn Gray
When you use double quotes in Android Studio and they turn gray, this typically indicates that the IDE recognizes the syntax but may not have proper context for resolution. This doesn’t necessarily mean the code is incorrect - it often means you need to ensure your project structure and version catalogs are properly configured.
Proper Version Catalog Configuration
Your second issue involves unresolved references to dependencies, which points to problems with your version catalog setup. The current error suggests that Android Studio cannot find the “android” and “compose” references in your version catalog.
File Structure Issues
First, there’s a naming convention issue. Gradle version catalogs should be named libs.versions.toml, not build.settings. This file should be located in your project’s gradle directory:
project-root/
├── gradle/
│ └── libs.versions.toml
├── app/
│ └── build.gradle.kts
└── settings.gradle.kts
Correct Version Catalog Structure
Your libs.versions.toml file should follow this structure:
[versions]
agp = "8.1.0"
kotlin = "1.9.0"
androidx-core = "1.12.0"
# Add other versions as needed
[libraries]
# Libraries section for regular dependencies
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
Key Differences in Your Configuration
Your current configuration has several issues:
- You’re using
plugin-prefixes, which aren’t standard conventions - You’re trying to reference plugins as libraries, which is incorrect
- The module paths for plugins are typically not needed this way
Proper Plugin References
In your build.gradle.kts files, you should reference plugins like this:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
Notice that we use alias(libs.plugins.<plugin-name>) rather than trying to reference them as libraries.
Plugin Application in Kotlin DSL
Now let’s dive deeper into how plugins should be applied in Kotlin DSL, especially in multi-module Android projects.
The Plugins Block
The plugins block in Kotlin DSL serves a dual purpose:
- It declares plugins that should be added to the build classpath
- It applies plugins to the current project
Here’s a comprehensive example:
plugins {
// Apply directly to current project
id("com.android.application")
kotlin("android")
// Add to classpath but don't apply
id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false
// Using version catalog aliases
alias(libs.plugins.kotlin.compose)
}
Multi-Module Plugin Management
In multi-module projects, it’s often better to centralize plugin management. You can do this in your settings.gradle.kts:
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("com.android.application") version "8.1.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
}
// Or using version catalogs
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
}
Convention Plugins for Multi-Module Projects
For complex multi-module projects, consider creating convention plugins:
- Create a
buildSrcdirectory in your project root - Add a
build.gradle.ktsfile inbuildSrc - Create your convention plugins in
src/main/kotlin
Example convention plugin:
// buildSrc/src/main/kotlin/AndroidApplicationConventionPlugin.kt
import com.android.build.gradle.AppExtension
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.kotlin.dsl.configure
class AndroidApplicationConventionPlugin : Plugin<Project> {
override fun apply(target: Project) {
with(target) {
with(pluginManager) {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
}
extensions.configure<AppExtension> {
compileSdk = 34
defaultConfig {
minSdk = 24
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
}
}
}
Dependency Management in Multi-Module Projects
Proper dependency management is crucial in multi-module Android projects. Let’s explore how to set this up correctly with Kotlin DSL and version catalogs.
Centralized Version Catalogs
Your libs.versions.toml should be the single source of truth for all dependency versions and coordinates:
[versions]
# Plugin versions
agp = "8.1.0"
kotlin = "1.9.0"
ksp = "1.9.0-1.0.13"
# Library versions
androidx-activity = "1.8.0"
androidx-compose = "1.5.4"
androidx-lifecycle = "2.6.2"
kotlinx-coroutines = "1.7.3"
[libraries]
# AndroidX libraries
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
# Kotlin libraries
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
Referencing Dependencies in Modules
Each module’s build.gradle.kts file should reference dependencies using the version catalog:
dependencies {
// Implementation dependencies
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.kotlinx.coroutines.android)
// API dependencies (for library modules)
api(project(":core-ui"))
// Test dependencies
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
}
Inter-Module Dependencies
For dependencies between modules in your project, use project references:
// In app/build.gradle.kts
dependencies {
implementation(project(":feature:auth"))
implementation(project(":feature:dashboard"))
implementation(project(":common:ui"))
implementation(project(":common:networking"))
}
Ensure your settings.gradle.kts includes all modules:
rootProject.name = "my-app"
include(":app")
include(":feature:auth")
include(":feature:dashboard")
include(":common:ui")
include(":common:networking")
Common Gradle Configuration Errors
Let’s address some of the most frequent gradle configuration errors in multi-module Android applications and how to resolve them.
Unresolved Reference Errors
The error “Unresolved reference” typically occurs when:
- Version catalog not found: Ensure your
libs.versions.tomlis in the correct location (gradle/directory) - Incorrect syntax: Verify you’re using
alias(libs.plugins.<name>)for plugins - Missing plugin management: Check your
settings.gradle.ktsfor proper plugin repositories
Example of correct plugin reference:
// Correct
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
// Incorrect - causes unresolved reference
plugins {
libs.plugins.android.application
}
Version Catalog Resolution Issues
If Android Studio shows “Unresolved reference” for version catalog entries:
- Refresh Gradle: Click “Sync Project with Gradle Files”
- Check file location: Verify
libs.versions.tomlis ingradle/directory - Verify syntax: Ensure TOML syntax is correct
- Check Android Studio cache: Sometimes IDE cache needs to be invalidated
Plugin Application Conflicts
When multiple plugins conflict or override each other:
// This order matters - later plugins can override earlier ones
plugins {
id("com.android.application") // Base Android plugin
kotlin("android") // Kotlin Android support
kotlin("compose") // Compose support
// Add compose-specific plugins after the base ones
}
Multi-Module Plugin Misconfiguration
Common issues in multi-module setups:
// settings.gradle.kts - correct way to manage plugins across modules
pluginManagement {
repositories {
google()
mavenCentral()
}
}
plugins {
// Apply to all subprojects
id("com.android.library") version "8.1.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
}
// Or using version catalogs
plugins {
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.android) apply false
}
Migration Strategies from Groovy to Kotlin DSL
Migrating from Groovy to Kotlin DSL in multi-module projects requires a systematic approach. Here’s how to do it effectively.
Gradual Migration Approach
- Start with settings files:
// settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
include(":app")
include(":core")
include(":feature:auth")
- Migrate version catalogs:
# gradle/libs.versions.toml
[versions]
agp = "8.1.0"
kotlin = "1.9.0"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
- Migrate individual modules, starting with shared ones:
// core/build.gradle.kts
plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.example.core"
compileSdk = 34
defaultConfig {
minSdk = 24
}
}
Automated Migration Tools
Consider using the Android Studio migration tools:
- Refactor > Migrate to Kotlin DSL
- Code > Convert Java/Kotlin File
- Gradle > Kotlin DSL > Convert Build Script to Kotlin DSL
Common Migration Pitfalls
- Plugin syntax changes:
// Old Groovy syntax
apply plugin: 'com.android.application'
// New Kotlin DSL syntax
plugins {
id 'com.android.application'
}
- Dependency configuration changes:
// Old Groovy syntax
implementation 'androidx.core:core-ktx:1.12.0'
// New Kotlin DSL with version catalog
implementation(libs.androidx.core.ktx)
- Android block syntax:
// Old Groovy syntax
android {
compileSdkVersion 34
defaultConfig {
minSdkVersion 24
}
}
// New Kotlin DSL
android {
compileSdk = 34
defaultConfig {
minSdk = 24
}
}
Best Practices for Multi-Module Projects
Implementing best practices will help you avoid common gradle configuration errors and maintain a clean, scalable project structure.
Project Structure Organization
my-android-app/
├── gradle/
│ └── libs.versions.toml
├── app/
│ └── build.gradle.kts
├── core/
│ └── build.gradle.kts
├── features/
│ ├── auth/
│ │ └── build.gradle.kts
│ └── dashboard/
│ └── build.gradle.kts
├── shared/
│ ├── ui/
│ │ └── build.gradle.kts
│ └── domain/
│ └── build.gradle.kts
├── settings.gradle.kts
└── build.gradle.kts
Centralized Configuration
Use a root-level build.gradle.kts for shared configuration:
// build.gradle.kts
subprojects {
apply(plugin = "kotlin-android")
android {
compileSdk = 34
}
kotlin {
jvmToolchain(8)
}
dependencies {
implementation(libs.kotlinx.coroutines.android)
}
}
Version Catalog Best Practices
- Use consistent naming:
[libraries]
# Use kebab-case for better code completion
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
- Organize logically:
[libraries]
# AndroidX libraries
androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "androidx-activity" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "androidx-activity" }
# Kotlin libraries
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }
- Document your catalog:
# gradle/libs.versions.toml
# Version catalog for MyApp dependencies
#
# Usage: implementation(libs.library-name)
[versions]
Plugin Management Strategy
- Centralize plugin versions:
// settings.gradle.kts
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
plugins {
id("com.android.application") version "8.1.0" apply false
id("org.jetbrains.kotlin.android") version "1.9.0" apply false
}
- Use version catalog aliases:
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
Build Performance Optimization
- Configure build cache:
// settings.gradle.kts
buildCache {
local {
directory = File(rootDir, "build-cache")
removeOlderThan = 24.hours
isEnabled = true
}
}
- Optimize dependency resolution:
// settings.gradle.kts
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
// build.gradle.kts
dependencies {
// Use implementation for internal dependencies
implementation(project(":shared:ui"))
// Use api for public APIs
api(project(":shared:domain"))
}
Troubleshooting Guide
When you encounter gradle configuration errors in your multi-module Android project, follow this systematic troubleshooting approach.
Issue 1: “Unresolved reference” in Version Catalog
Symptoms: Android Studio shows red underlines for libs.plugins.android.application or similar references.
Solution:
- Verify
libs.versions.tomlis in the correct location (gradle/directory) - Check file is included in your project:
// settings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("gradle/libs.versions.toml"))
}
}
}
- Refresh Gradle: Click “Sync Project with Gradle Files”
- Invalidate caches: File > Invalidate Caches / Restart
Issue 2: Plugin Application Conflicts
Symptoms: Build fails with “Plugin already applied” or similar errors.
Solution:
- Check for duplicate plugin applications:
// Both of these apply the same plugin - causes conflict
plugins {
id("com.android.application")
}
android {
// ...
}
// Correct approach - apply once in plugins block
plugins {
id("com.android.application")
}
android {
// ...
}
- Use
apply falsefor conditional application:
// settings.gradle.kts
plugins {
id("com.android.library") version "8.1.0" apply false
}
Issue 3: Multi-Module Dependency Resolution
Symptoms: “Cannot resolve symbol” for project dependencies.
Solution:
- Verify module inclusion in
settings.gradle.kts:
// settings.gradle.kts
include(":app")
include(":feature:auth")
include(":shared:ui")
- Check dependency syntax in modules:
// In app/build.gradle.kts
dependencies {
implementation(project(":feature:auth"))
implementation(project(":shared:ui"))
}
- Ensure consistent module naming across all references.
Issue 4: Kotlin DSL Syntax Errors
Symptoms: Syntax highlighting errors or compilation failures.
Solution:
- Use proper Kotlin DSL syntax:
// Correct
android {
compileSdk = 34
namespace = "com.example.app"
}
// Incorrect - causes syntax error
android {
compileSdkVersion 34
package_name "com.example.app"
}
- Use proper plugin syntax:
// Correct
plugins {
alias(libs.plugins.android.application)
}
// Incorrect
plugins {
"com.android.application"
}
Issue 5: Version Catalog Not Found
Symptoms: Gradle sync fails with “Could not resolve catalog”.
Solution:
- Verify file structure:
project-root/
├── gradle/
│ └── libs.versions.toml
└── settings.gradle.kts
-
Check file permissions and ensure it’s not accidentally hidden.
-
Verify TOML syntax is correct:
[versions]
agp = "8.1.0"
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
Issue 6: Build Performance Issues
Symptoms: Gradle builds are slow or inconsistent.
Solution:
- Configure parallel builds:
// gradle.properties
org.gradle.parallel=true
org.gradle.caching=true
- Optimize dependency resolution:
// settings.gradle.kts
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
// build.gradle.kts
repositories {
google()
mavenCentral()
}
- Use build cache effectively:
// settings.gradle.kts
buildCache {
local {
directory = File(rootDir, "build-cache")
isEnabled = true
}
}
Sources
-
Migrate your build configuration from Groovy to Kotlin DSL — Official Android documentation on Kotlin DSL migration: https://developer.android.com/build/migrate-to-kotlin-dsl
-
Migrate your build to version catalogs — Official Android documentation on version catalog configuration: https://developer.android.com/build/migrate-to-catalogs
-
Getting Started with the Kotlin DSL — Gradle’s official guide to Kotlin DSL implementation: https://gradle.com/blog/kotlin-dsl/
-
Migrating to Kotlin DSL & Version Catalog — Practical migration guide with examples: https://notificare.com/blog/2023/11/10/migrating-to-kotlin-dsl-and-version-catalog/
-
Understanding TOML Files in Android Gradle Configuration — Detailed explanation of TOML syntax in Android projects: https://medium.com/@auvehassan/understanding-toml-files-in-android-gradle-configuration-with-build-gradle-kts-examples-7d10e05a58de
-
Different ways to apply plugins (Gradle Kotlin DSL) — Community guide on plugin application methods: https://stackoverflow.com/questions/48290389/different-ways-to-apply-plugins-gradle-kotlin-dsl
-
Unresolved reference: KotlinCompilerVersion in build.gradle.kts — Stack Overflow solution for Kotlin DSL version references: https://stackoverflow.com/questions/55619577/unresolved-reference-kotlincompilerversion-in-build-gradle-kts
-
Android Gradle Kotlin DSL multi-module seeking help — Community discussion on multi-module configuration challenges: https://www.reddit.com/r/androiddev/comments/1529g14/android_gradle_kotlindsl_multimodule_seeking_help/
-
Unresolved reference: android — Gradle issue tracker with plugin resolution solutions: https://github.com/gradle/kotlin-dsl-samples/issues/1393
Conclusion
Resolving gradle configuration errors in multi-module Android applications requires understanding both Kotlin DSL syntax rules and proper version catalog management. The key takeaways are:
- Always use double quotes in Kotlin DSL strings, not single quotes
- Name your version catalog file
libs.versions.tomland place it in thegradle/directory - Reference plugins using
alias(libs.plugins.<name>)syntax - Centralize plugin and dependency management in version catalogs
- Apply plugins conditionally with
apply falsein multi-module projects - Use proper project references for inter-module dependencies
- Follow a systematic migration approach when switching from Groovy to Kotlin DSL
By implementing these practices, you’ll eliminate the most common gradle configuration errors and create a more maintainable, scalable Android project structure. Remember to leverage Android Studio’s migration tools and always verify your project structure matches Gradle’s expectations for multi-module setups.