Mobile Dev

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.

1 answer 1 view

How to resolve Gradle configuration errors in a multi-module Android application? I’m experiencing two main issues:

  1. When using the Kotlin DSL in my build.gradle file with the plugins block:
groovy
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.

  1. In my build.gradle file, I’m trying to reference dependencies defined in my build.settings file:
groovy
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:

toml
[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:

kotlin
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:

kotlin
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:

kotlin
plugins {
 id("com.android.application")
 kotlin("android")
 kotlin("compose")
}

For multi-module projects, you might want to apply plugins conditionally:

kotlin
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:

toml
[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:

  1. You’re using plugin- prefixes, which aren’t standard conventions
  2. You’re trying to reference plugins as libraries, which is incorrect
  3. 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:

kotlin
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:

  1. It declares plugins that should be added to the build classpath
  2. It applies plugins to the current project

Here’s a comprehensive example:

kotlin
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:

kotlin
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:

  1. Create a buildSrc directory in your project root
  2. Add a build.gradle.kts file in buildSrc
  3. Create your convention plugins in src/main/kotlin

Example convention plugin:

kotlin
// 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:

toml
[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:

kotlin
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:

kotlin
// 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:

kotlin
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:

  1. Version catalog not found: Ensure your libs.versions.toml is in the correct location (gradle/ directory)
  2. Incorrect syntax: Verify you’re using alias(libs.plugins.<name>) for plugins
  3. Missing plugin management: Check your settings.gradle.kts for proper plugin repositories

Example of correct plugin reference:

kotlin
// 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:

  1. Refresh Gradle: Click “Sync Project with Gradle Files”
  2. Check file location: Verify libs.versions.toml is in gradle/ directory
  3. Verify syntax: Ensure TOML syntax is correct
  4. Check Android Studio cache: Sometimes IDE cache needs to be invalidated

Plugin Application Conflicts

When multiple plugins conflict or override each other:

kotlin
// 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:

kotlin
// 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

  1. Start with settings files:
kotlin
// settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}

include(":app")
include(":core")
include(":feature:auth")
  1. Migrate version catalogs:
toml
# 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" }
  1. Migrate individual modules, starting with shared ones:
kotlin
// 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:

  1. Refactor > Migrate to Kotlin DSL
  2. Code > Convert Java/Kotlin File
  3. Gradle > Kotlin DSL > Convert Build Script to Kotlin DSL

Common Migration Pitfalls

  1. Plugin syntax changes:
kotlin
// Old Groovy syntax
apply plugin: 'com.android.application'

// New Kotlin DSL syntax
plugins {
id 'com.android.application'
}
  1. Dependency configuration changes:
kotlin
// Old Groovy syntax
implementation 'androidx.core:core-ktx:1.12.0'

// New Kotlin DSL with version catalog
implementation(libs.androidx.core.ktx)
  1. Android block syntax:
kotlin
// 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:

kotlin
// build.gradle.kts
subprojects {
 apply(plugin = "kotlin-android")
 
 android {
 compileSdk = 34
 }
 
 kotlin {
 jvmToolchain(8)
 }
 
 dependencies {
 implementation(libs.kotlinx.coroutines.android)
 }
}

Version Catalog Best Practices

  1. Use consistent naming:
toml
[libraries]
# Use kebab-case for better code completion
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
  1. Organize logically:
toml
[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" }
  1. Document your catalog:
toml
# gradle/libs.versions.toml
# Version catalog for MyApp dependencies
# 
# Usage: implementation(libs.library-name)

[versions]

Plugin Management Strategy

  1. Centralize plugin versions:
kotlin
// 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
}
  1. Use version catalog aliases:
toml
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

Build Performance Optimization

  1. Configure build cache:
kotlin
// settings.gradle.kts
buildCache {
local {
directory = File(rootDir, "build-cache")
removeOlderThan = 24.hours
isEnabled = true
}
}
  1. Optimize dependency resolution:
kotlin
// 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:

  1. Verify libs.versions.toml is in the correct location (gradle/ directory)
  2. Check file is included in your project:
kotlin
// settings.gradle.kts
dependencyResolutionManagement {
versionCatalogs {
create("libs") {
from(files("gradle/libs.versions.toml"))
}
}
}
  1. Refresh Gradle: Click “Sync Project with Gradle Files”
  2. Invalidate caches: File > Invalidate Caches / Restart

Issue 2: Plugin Application Conflicts

Symptoms: Build fails with “Plugin already applied” or similar errors.

Solution:

  1. Check for duplicate plugin applications:
kotlin
// Both of these apply the same plugin - causes conflict
plugins {
id("com.android.application")
}

android {
// ...
}
kotlin
// Correct approach - apply once in plugins block
plugins {
id("com.android.application")
}

android {
// ...
}
  1. Use apply false for conditional application:
kotlin
// 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:

  1. Verify module inclusion in settings.gradle.kts:
kotlin
// settings.gradle.kts
include(":app")
include(":feature:auth")
include(":shared:ui")
  1. Check dependency syntax in modules:
kotlin
// In app/build.gradle.kts
dependencies {
implementation(project(":feature:auth"))
implementation(project(":shared:ui"))
}
  1. Ensure consistent module naming across all references.

Issue 4: Kotlin DSL Syntax Errors

Symptoms: Syntax highlighting errors or compilation failures.

Solution:

  1. Use proper Kotlin DSL syntax:
kotlin
// Correct
android {
compileSdk = 34
namespace = "com.example.app"
}

// Incorrect - causes syntax error
android {
compileSdkVersion 34
package_name "com.example.app"
}
  1. Use proper plugin syntax:
kotlin
// 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:

  1. Verify file structure:
project-root/
├── gradle/
│ └── libs.versions.toml
└── settings.gradle.kts
  1. Check file permissions and ensure it’s not accidentally hidden.

  2. Verify TOML syntax is correct:

toml
[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:

  1. Configure parallel builds:
kotlin
// gradle.properties
org.gradle.parallel=true
org.gradle.caching=true
  1. Optimize dependency resolution:
kotlin
// settings.gradle.kts
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")

// build.gradle.kts
repositories {
google()
mavenCentral()
}
  1. Use build cache effectively:
kotlin
// settings.gradle.kts
buildCache {
local {
directory = File(rootDir, "build-cache")
isEnabled = true
}
}

Sources


  1. 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

  2. Migrate your build to version catalogs — Official Android documentation on version catalog configuration: https://developer.android.com/build/migrate-to-catalogs

  3. Getting Started with the Kotlin DSL — Gradle’s official guide to Kotlin DSL implementation: https://gradle.com/blog/kotlin-dsl/

  4. 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/

  5. 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

  6. 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

  7. 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

  8. 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/

  9. 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:

  1. Always use double quotes in Kotlin DSL strings, not single quotes
  2. Name your version catalog file libs.versions.toml and place it in the gradle/ directory
  3. Reference plugins using alias(libs.plugins.<name>) syntax
  4. Centralize plugin and dependency management in version catalogs
  5. Apply plugins conditionally with apply false in multi-module projects
  6. Use proper project references for inter-module dependencies
  7. 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.

Authors
Verified by moderation
Moderation
Resolving Gradle Configuration Errors in Multi-Module Android Apps