Published on

Gradle 9.7 Upgrade Guide: Breaking Changes, Isolated Projects, and Verification

Authors

Gradle 9.7.0 was released on August 6, 2026. Its headline feature is the promotion of Isolated Projects from experimental to incubating, but the release is broader than one performance flag. It changes embedded Kotlin, Kotlin DSL compilation, publishing output, parameterless action behavior, file-system watching, test diagnostics, and several deprecation paths toward Gradle 10.

The safest upgrade is therefore not “change the wrapper and turn on every new feature.” It is a staged migration:

  1. upgrade the wrapper with existing behavior;
  2. resolve compatibility and deprecation failures;
  3. verify tests, publications, dependency resolution, and caches;
  4. evaluate Isolated Projects separately in diagnostics mode;
  5. enable it only where the build and plugin ecosystem are compatible.

TL;DR

  • Gradle 9.7.0 is a stable release from August 6, 2026.
  • Isolated Projects is now incubating, not a default production recommendation.
  • Kotlin DSL scripts can no longer import Gradle's relocated org.gradle.internal.impldep.* classes.
  • Embedded Kotlin moves to 2.4.0, where language version 1.9 is no longer supported.
  • Publishing no longer creates checksum files for .asc and .sig signature files.
  • Upgrade and verify the wrapper first; evaluate Isolated Projects in a separate change.

Start with the wrapper, not a global installation

The Gradle Wrapper keeps developer machines and CI on the same distribution. The official 9.7 release notes recommend updating it with:

./gradlew :wrapper --gradle-version=9.7.0 && ./gradlew :wrapper

Running the task twice updates both the wrapper configuration and the wrapper scripts or JAR when necessary. Review the resulting diff rather than assuming that only gradle-wrapper.properties will change.

Before the update, capture a baseline:

./gradlew --version
./gradlew help --warning-mode all
./gradlew test
./gradlew build

For a multi-project build, include the tasks used by CI and release automation. build does not necessarily exercise custom publishing, code generation, integration tests, Android variants, or deployment plugins.

After updating the wrapper, confirm the actual distribution:

./gradlew --version

Do not rely on a system gradle --version; it may report a different installation than ./gradlew uses.

Check Java, Kotlin, Groovy, and Android compatibility first

Gradle's own runtime requirements and the compatibility of plugins are separate questions. A supported JDK can run Gradle while an older plugin still binds to an internal API that changed.

Inventory the build environment:

java -version
./gradlew --version
./gradlew buildEnvironment

Then compare the repository's Java, Kotlin, Groovy, Android Gradle Plugin, and plugin versions with Gradle's compatibility matrix and each plugin's release notes.

This is especially important when upgrading from earlier than 9.6. Gradle's upgrade guide notes that some plugins relying on removed Problems API internals—including older Android Gradle Plugin lines—can fail before 9.7-specific changes are reached. Apply the intervening 9.x migration guidance in order.

Potential breaking change: embedded Kotlin 2.4.0

Gradle 9.7 upgrades its embedded Kotlin from 2.3.21 to 2.4.0. Starting with Kotlin 2.4.0, -language-version=1.9 is no longer supported; the old K1 compiler path is gone.

Search build logic and convention plugins for old language targets:

rg 'languageVersion|apiVersion|1\.9|kotlinOptions' \
  build.gradle build.gradle.kts settings.gradle settings.gradle.kts buildSrc gradle

If a convention plugin intentionally targets an older Kotlin level, distinguish the Kotlin version used to compile the plugin from the embedded Kotlin used by Gradle's Kotlin DSL. Do not change an application's production Kotlin target simply to silence a build-script error.

Run Kotlin DSL compilation explicitly through normal configuration:

./gradlew help --stacktrace

Configuration happens before task execution, so help is a fast way to expose script-compilation and plugin-application failures without running the entire build.

Potential breaking change: internal Kotlin DSL imports

Kotlin DSL scripts are now compiled against a prebuilt public Gradle API JAR. Imports from Gradle's relocated internal dependencies no longer compile:

// Before: internal API, fails on Gradle 9.7
import org.gradle.internal.impldep.com.google.gson.Gson

Replace the internal type with an explicit build-logic dependency and its public package:

// buildSrc/build.gradle.kts or an included build
dependencies {
    implementation("com.google.code.gson:gson:2.11.0")
}
import com.google.gson.Gson

Search for the internal prefix before upgrading:

rg 'org\.gradle\.internal\.impldep' .

Depending on Gradle's relocated libraries was never a supported contract. Pinning the external dependency makes the build logic's requirements explicit and prevents a future Gradle distribution change from silently replacing the library version.

Potential breaking change: publishing signature checksums

Gradle 9.7 stops publishing checksum files such as .sha1 and .md5 for signature artifacts like .asc and .sig. The signature artifacts themselves are still published.

Most Maven consumers do not need checksums of signature files. A custom release verifier, repository promotion script, or artifact inventory may still assume that files such as these exist:

library-1.0.jar.asc.sha1
library-1.0.pom.asc.sha1

Inspect the staging repository after a test publication and compare its artifact list with the previous release:

./gradlew publishToMavenLocal
find ~/.m2/repository/com/example -type f | sort

Use a disposable group and version for verification. Do not overwrite a real release in a remote repository merely to test the migration.

If downstream automation fails, update it to validate the primary artifacts, their normal checksums, and the signature files directly. Recreating signature-checksum files in Gradle just to preserve an unnecessary assumption is usually the wrong fix.

Potential behavior change: parameterless actions

Gradle standardizes action types that use WorkParameters.None, TransformParameters.None, BuildServiceParameters.None, ValueSourceParameters.None, and related markers.

Two behaviors matter:

  • configuration actions now run even for a None parameter type;
  • getParameters() returns the None singleton rather than null.

Typical builds need no source change. Custom plugins may fail if they use a null check to detect parameterless work:

// Fragile before/after assumption
if (getParameters() == null) {
    // parameterless path
}

Use the declared marker type instead:

if (getParameters() instanceof WorkParameters.None) {
    // parameterless path
}

Also inspect configuration lambdas passed to parameterless actions. A block that Gradle previously skipped can now execute, exposing an unintended side effect.

File-system watching with a custom project cache

Gradle no longer watches the project cache directory itself because Gradle is its only expected writer. This removes a previous incompatibility: using --project-cache-dir no longer disables file-system watching for the rest of the build.

This can improve long-running local and CI agents that use a custom cache location. It also means tests should distinguish the project cache from source-dependency checkouts, which remain watched as normal build directories.

Verify the setup with the same flags used in CI:

./gradlew build --project-cache-dir .ci-gradle-cache --watch-fs --info

Run it in a disposable checkout. A project-cache path may contain machine-specific and transient state and should not be committed.

What Isolated Projects actually promises

The configuration cache can skip project configuration when a compatible cached state is reusable. Isolated Projects targets the configuration work that still must happen, including IDE synchronization, by making it safe to configure sibling projects in parallel.

Gradle 9.7 promotes the feature to incubating. The official release notes explicitly say it is ready for early adopters and feedback, is not enabled by default, and is not yet recommended for production use.

Enable it only for an evaluation:

./gradlew help --isolated-projects

Or use the new property name:

# gradle.properties
org.gradle.isolated-projects=true

The earlier org.gradle.unsafe.isolated-projects names remain aliases but are deprecated. Replace them with:

org.gradle.isolated-projects=true
org.gradle.isolated-projects.diagnostics=true

Do not begin with dangerously-ignore-problems. That option can help estimate a performance ceiling during migration, but it relaxes safety constraints and should not become a permanent CI setting.

Why isolation can break existing build logic

Project isolation prevents one project's configuration logic from reaching into mutable state owned by another project or by the whole build. Patterns such as this violate the model:

project(":other").tasks.named("build")

Prefer explicit provider-based dependencies and published project outputs:

dependencies {
    implementation(project(":other"))
}

The exact replacement depends on the intent. A task dependency, a variant-aware artifact dependency, and a convention shared through a plugin are not interchangeable. Use diagnostics to locate each cross-project access, explain what data is being shared, and choose the correct public model.

An Isolated Projects migration should also begin from a build that is compatible with the configuration cache. Otherwise the team is debugging two overlapping configuration models at once.

Configuration Cache improvements in 9.7

Gradle 9.7 makes ResolutionResult fully compatible as a task input for the configuration cache. Custom tasks that analyze dependency graphs can use the higher-level result instead of manually copying parts of the graph into serializable fields.

The release also improves Java-agent behavior with TestKit and removes a source of spurious cache invalidation triggered by an IntelliJ IDEA system property.

These improvements do not make every task cache-compatible. Verify the real build:

./gradlew build --configuration-cache
./gradlew build --configuration-cache

The first run stores the configuration state. The second run should report reuse. Review the generated configuration-cache report for tasks or build logic that capture unsupported state.

Do not compare only wall-clock time. A fast second run that silently skips necessary inputs is a correctness bug, not a successful optimization.

Test reporting and TestNG compatibility

Gradle now reports framework-initialization failures from TestNG, JUnit 4, and JUnit Platform in the console by default. This is particularly useful when a test class cannot be instantiated: the failure is less likely to appear as an unexplained empty test run.

For TestNG, Gradle 9.7 supports both sides of the thread-pool factory API change introduced in TestNG 7.10. The configured class must implement the interface exposed by the TestNG version actually on the test runtime classpath.

Check the resolved version and run a representative parallel suite:

./gradlew dependencyInsight \
  --dependency org.testng:testng \
  --configuration testRuntimeClasspath

./gradlew test --info

If a custom threadPoolFactoryClass fails, verify its interface against the resolved TestNG version instead of changing Gradle's test worker count as a workaround.

Preview dependency ordering separately

Gradle 9.7 adds the ENHANCED_GRAPH_ORDERING feature preview for dependency result ordering expected to become the default in Gradle 10:

// settings.gradle.kts
enableFeaturePreview("ENHANCED_GRAPH_ORDERING")

This is not required for the 9.7 upgrade. Enable it in a separate change if custom reports, SBOM tools, lock generation, or plugin tests depend on graph traversal order.

Compare outputs semantically. A reordered report with the same components may be correct, while a snapshot test that assumes an incidental sequence may need to sort its own presentation layer.

A practical verification matrix

Run the checks that match the repository rather than relying on one generic build task.

AreaVerification
Wrapper./gradlew --version reports 9.7.0 on developer and CI machines
Configuration./gradlew help --warning-mode all succeeds
CompilationClean Java, Kotlin, Groovy, and generated-source builds succeed
TestsUnit, integration, TestKit, and parallel TestNG suites run
DependenciesLockfiles, verification metadata, and reports contain expected modules
PublishingA staging or local publication has the intended files and signatures
Configuration CacheA second compatible run reuses the stored state
IDEImport and sync complete without cross-project configuration errors
Isolated ProjectsDiagnostics are reviewed before any broader enablement

For repositories that use Maven as well as Gradle—for example, while migrating build systems—keep local dependency cache troubleshooting separate from wrapper compatibility. See Maven Dependency Cache Troubleshooting for Maven-specific cache repair and verification.

Rollout and rollback

A wrapper upgrade should be its own reviewable commit. Do not combine it with broad dependency upgrades, Kotlin source rewrites, and Isolated Projects enablement.

A controlled rollout looks like this:

  1. update the wrapper to 9.7.0;
  2. make only the compatibility fixes required for the existing build;
  3. run CI and a test publication;
  4. merge and monitor build duration and cache behavior;
  5. evaluate configuration cache improvements;
  6. evaluate Isolated Projects in a later change.

Rollback means restoring the previously verified wrapper files and any 9.7-specific build-logic changes together. Retaining new property names or code that depends on 9.7 APIs while reverting only the distribution URL creates a mixed state that was never tested.

Gradle 9.7 is a useful stable release, particularly for large multi-project builds preparing for safer parallel configuration. Its performance headline should not obscure the migration details. Upgrade the stable wrapper first, treat incubating features as experiments, and let reproducible build output—not a single timing number—decide when the migration is complete.

Official references