We are excited to announce Gradle 9.9.0 (released 2026-11-06).
In this release, Kotlin DSL scripts benefit from incremental compilation, making script recompilation after a change in buildSrc or an included build up to 65% faster in large multi-project builds.
Test filters now also apply to JUnit Platform tests that are not declared as methods, such as ArchUnit rules and Spek scopes, so --tests runs only the tests you select.
The wrapper task now keeps your customized properties when you upgrade Gradle.
In CLI, logging, and problem reporting, the Problems API groups problems by area, such as Compilation > Java, so you can see where each one comes from, and Windows Terminal now shows build progress on the taskbar. Isolated Projects now also works with Eclipse, so you can sync builds that have it enabled in Buildship as well as in IntelliJ IDEA and Android Studio.
Build authors get new ProviderFactory methods for values that are already known, and plugin authors can now make the Sync task empty its destination when its source is empty.
Finally, the User Manual is now available as Markdown, so AI agents and other tools can read it directly, and includes a new chapter on securing Gradle builds.
We would like to thank the following community members for their contributions to this release of Gradle: Aurimas, devareddy05, DragonFSKY, fru1tworld, Ivan Khanas, Jeremy Schoemaker, John Burns, Labh R Jethe, Marcin Mycek, monkey, Nelson Osacky, Roberto Perez Alcolea, Róbert Papp, SeungTaek Lim, Stefan Oehme, 万彬宇, 한의준.
Be sure to check out the public roadmap for insight into what's planned for future releases.
Switch your build to use Gradle 9.9.0 by updating the wrapper in your project:
./gradlew :wrapper --gradle-version=9.9.0 && ./gradlew :wrapper
See the Gradle 9.x upgrade guide to learn about deprecations, breaking changes, and other considerations when upgrading to Gradle 9.9.0.
For Java, Groovy, Kotlin, and Android compatibility, see the full compatibility notes.
Gradle continuously improves build performance through caching, parallelism, and reduced overhead across all phases of the build.
Kotlin DSL scripts are now compiled using the Kotlin Build Tools API, making incremental compilation available for build scripts.

When a script has to be recompiled, for example, after a signature change in buildSrc or an included build, only the affected parts are compiled again. In one of our performance tests on a large multi-project build, this makes recompiling scripts 65% faster.
Builds that rarely recompile scripts see no change.
Gradle provides a set of features and abstractions for testing JVM code, along with test reports to display results.
Test filters now apply to JUnit Platform tests that are not declared as methods, such as ArchUnit rules declared on fields with @ArchTest, or Spek scopes.
Previously, Gradle ran these tests under any filter unless their whole class was excluded, so they ran even when you filtered for something else:
# Previously also ran every ArchUnit rule in the source set; now runs only SomeUnrelatedTest
./gradlew test --tests "SomeUnrelatedTest"
Gradle now matches these tests by their enclosing class and the name they are reported with, the same way it matches test methods by class and method name.
Rules grouped with ArchTests.in(...) are matched by the grouped class instead of the field that holds them:
# Runs only the rule declared in the `firstRule` field of ArchRulesTest
./gradlew test --tests "ArchRulesTest.firstRule"
# Runs a rule grouped with ArchTests.in(NestedRules.class)
./gradlew test --tests "ArchRulesTest.NestedRules > nestedRule"
Tests that an engine registers only during execution, as Kotest does, can still only be selected by their spec class.
This also improves the Test Retry plugin, which uses test filters to select tests to rerun.
Previously, it reran every non-method-based test whenever any test in the task was retried, even ones that had passed. Now it reruns only the failed ones.
See the Upgrade Guide for breaking changes, and the Filtering Non-Method Tests section in the Gradle User Manual for more details.
Gradle provides various incremental updates to ensure the continued reliability of the build ecosystem.
wrapper task preserves customized propertiesThe wrapper task now preserves values from an existing gradle-wrapper.properties file when you regenerate the wrapper, for example, with ./gradlew wrapper --gradle-version 9.9.0.
Previously, the task overwrote them with defaults.
Gradle preserves the distribution type (bin or all), the network timeout, retries, and retry backoff, URL validation, and the distribution and archive paths and bases.
Values configured on the task or passed on the command line still take precedence.
The distributionUrl is regenerated for the new Gradle version, so a custom host is not preserved.
URL validation is preserved like any other value, so a single --no-validate-url run disables validation until you run the task again with --validate-url.
User-declared Wrapper tasks now also write the default network timeout, retry count, and retry backoff when those properties are not otherwise configured.
See the Preserving Existing Wrapper Properties section in the Gradle User Manual for more details.
Gradle provides an intuitive command-line interface, detailed logs, and a structured problems report that helps developers quickly identify and resolve build issues.
The incubating Problems API now provides a predefined hierarchy of problem groups, available from the Problems service as problems.groups.
Eleven root groups cover the areas a problem can come from, each with predefined subgroups: Compilation > Java, Dependencies > Declaration, Packaging > Signing, Provisioning > Tools and Toolchains, Gradle > Plugin Validation, and so on. Plugins report into the matching predefined group or add a subgroup below it. The types of hierarchy enforce placement:
problems.reporter.report(problems.groups.compilation.java.problemId("Unused import")) {}
problems.reporter.report(problems.groups.transformation.group("KMP").problemId("Bundle failed")) {}
Each predefined group carries a description of what belongs in it, so consumers of problem reports have a documented set of group names to navigate, filter, and aggregate by.
On the console, problems now show the chain of groups they belong to, for example, Unused import (in Compilation > Java). The Groups tab of the HTML problems report lists the root groups in the order of the predefined hierarchy and shows the description of each predefined group.
The existing ProblemGroup.create() and ProblemId.create() methods continue to work, and a group created with them whose name matches a predefined group is now reported as that predefined group; migrating to predefined groups is recommended.

The legacy problem groups are gone, and both the groups and the names of Gradle's own problem IDs have changed, so Tooling API clients, Build Scans, and any tooling that matches problems by ID need to update.
See the upgrade guide for the complete list of changed problem IDs:
See the Predefined Problem Groups section in the Gradle User Manual for more details.
When Gradle runs in Windows Terminal 1.6 or later, the build progress shown in the console now also appears on the Windows taskbar icon, including in WSL shells hosted by Windows Terminal. This shows how far a long-running build has progressed while the terminal is in the background.

Gradle already reports progress this way to ConEmu, Ghostty, kitty, and iTerm2.
See the Rich console section in the Gradle User Manual for more details.
Isolated Projects is an incubating performance feature that safely runs project configuration in parallel, significantly reducing configuration time in many scenarios, including IDE sync and CI builds.
Buildship, the Gradle integration for Eclipse, can now synchronize builds that have Isolated Projects enabled.
Previously, synchronizing such a build in Eclipse failed because the Eclipse tooling models read the state of other projects in a way that Isolated Projects reports as a constraint violation. Gradle now builds these models without breaking project isolation, so Eclipse users can adopt Isolated Projects alongside users of IntelliJ IDEA and Android Studio.
Builds that do not enable Isolated Projects see no change.
See the Isolated Projects section in the Gradle User Manual for more details.
Gradle provides rich APIs for build engineers and plugin authors, enabling the creation of custom, reusable build logic and better maintainability.
Build logic often has a value at hand and needs to pass it to an API that accepts a Provider. Previously, this required wrapping the value in a Callable with providers.provider { value }, or providers.provider { null } for a provider that has no value.
ProviderFactory now has three incubating methods for these cases:
absent() returns a provider that never has a value.present(value) returns a provider that always has the given value.presentIfNotNull(value) returns a provider that has the given value when it is not null, and has no value otherwise.val missing: Provider<String> = providers.absent()
val name: PresentProvider<String> = providers.present("my-lib")
val value: Provider<String> = providers.presentIfNotNull(nullableValue)
Unlike provider(Callable), these methods do not compute the value on demand, so prefer them when the value is already known.
present(value) returns the new PresentProvider type, a Provider that is guaranteed to have a value: its get() never fails, and its getOrNull() never returns null.
See the Lazy Objects API Reference section in the Gradle User Manual for more details.
Gradle provides a comprehensive plugin system, including built-in Core Plugins for standard tasks and powerful APIs for creating custom plugins.
Sync can empty its destination when its source is emptyA Sync task works like Copy, but its destination directory contains only the files it copied; anything else in the destination is deleted. Sync now has an incubating skipWhenSourceIsEmpty property. Set it to false to make the task run when its source is empty, so the destination directory ends up empty too:
tasks.named<Sync>("mySync") {
skipWhenSourceIsEmpty = false
}
By default, Sync still skips the task when its source has no files or directories. Gradle then cleans up the destination only if the build owns it, such as a directory under build/. Any other destination keeps files that the source no longer contains.
Sync always deletes the entire contents of its destination, not only the files it copied there. With skipWhenSourceIsEmpty disabled, an empty source also deletes everything, even when the source is empty by mistake. Disable it only for destinations that no other task or tool writes to, and use preserve { ... } to keep files the task does not manage.
See Synchronizing from an empty source in the Gradle User Manual for more details.
Promoted features are features that were incubating in previous versions of Gradle but are now supported and subject to backward compatibility. See the User Manual section on the "Feature Lifecycle" for more information.
The following are the features that have been promoted in this Gradle release.
dependencies block APIThe remaining incubating parts of the API for custom dependencies blocks have been promoted:
add(ProviderConvertible) and the bundle(...) methods in DependencyCollectorconstraint(Provider) and constraint(ProviderConvertible) in Dependenciesmodify(ProviderConvertible) in DependencyModifierDependencyConstraintFactoryPlatformDependencyModifiers and TestFixturesDependencyModifiersAndSpec.findUnsatisfiedSpecAndSpec.findUnsatisfiedSpec(T) has been promoted. Use it to find the first member spec that an object does not satisfy.
Every page of the User Manual is now also available as Markdown, so AI agents and other tools can read the documentation directly. Replace .html with .md in a page's URL, for example, userguide/gradle_wrapper.md.
A new chapter, Securing Gradle Builds, covers securing the build tool, repositories, and dependencies.
The rewrite of the core plugin reference pages continues with the Java, Java Library, Java Platform, Java Test Fixtures, JVM Test Suite, JaCoCo, War, Distribution, and Version Catalog plugins. The Build Init plugin page now documents the test frameworks it supports.
Other notable additions:
WorkParameters property types.A new entry has been added to the Best Practices:
String values for custom attributes over Named types, since attribute values are published as plain strings.Known issues are problems that were discovered post-release that are directly related to changes made in this release.
We love getting contributions from the Gradle community. For information on contributing, please see gradle.org/contribute.
If you find a problem with this release, please file a bug on GitHub Issues adhering to our issue guidelines. If you're not sure if you're encountering a bug, please use the forum.
We hope you will build happiness with Gradle, and we look forward to your feedback via Twitter or on GitHub.