We are excited to announce Gradle 8.14 (released 2025-04-25).
Gradle now supports Java 24.
This release adds support for selecting GraalVM Native Image toolchains, and includes enhancements to the test report when tests are skipped, for example, because of assumptions.
Gradle 8.14 introduces lazy dependency configuration initialization to improve configuration performance and memory usage. The Problems API is expanded to support arbitrary structured data, making it easier for IDEs to consume rich diagnostics through the Tooling API.
Additionally, the configuration cache includes a new integrity check mode for improved debugging.
We would like to thank the following community members for their contributions to this release of Gradle: Aurimas, Ben Bader, Björn Kautler, chandre92, Daniel Hammer, Danish Nawab, Florian Dreier, Ivy Chen, Jendrik Johannes, jimmy1995-gu, Madalin Valceleanu, Na Minhyeok.
Be sure to check out the public roadmap for insight into what's planned for future releases.
Switch your build to use Gradle 8.14 by updating the Wrapper in your project:
./gradlew wrapper --gradle-version=8.14 && ./gradlew wrapper
See the Gradle 8.x upgrade guide to learn about deprecations, breaking changes, and other considerations when upgrading to Gradle 8.14.
For Java, Groovy, Kotlin, and Android compatibility, see the full compatibility notes.
With this release, Gradle supports Java 24. This means you can now use Java 24 for the daemon in addition to toolchains.
Third-party tool compatibility with Java 24 may still be limited. If you're using the Tooling API, you’ll need to enable native access at startup due to its use of JNI. See JEP 472 for details.
See the compatibility documentation for more details.
Gradle's toolchain support allows provisioning and selection of specific JDK versions for building projects—compiling code, running tests, and even running Gradle itself.
With this release, toolchain selection has been expanded to support GraalVM Native Image capability:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
nativeImageCapable = true
}
}
This allows Gradle to select only JDKs that support Native Image when resolving a toolchain. See the toolchain documentation for more details.
Note: Native Image capability selection is also supported for the daemon toolchain.
When a test is skipped due to an assumption violation, Gradle now includes the reason in both the HTML and JUnit XML reports. This applies to JUnit 4, JUnit Platform, and TestNG. For example, JUnit Platform provides an Assumptions API to conditionally skip tests:
package org.example;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assumptions.*;
class LibraryTest {
@Test
void someLibraryMethodReturnsTrue() {
assumeTrue(canExecute(), "missing requirements to run this test");
// Code for the rest of the test
}
}
If the assumeTrue check fails, the reason ("missing requirements to run this test") is now visible in the XML and HTML test report:
This feature was contributed by Ivy Chen with Aurimas Liutikas.
Gradle provides rich APIs for plugin authors and build engineers to develop custom build logic.
Just like tasks, dependency configurations are now realized only when necessary.
Starting with this release, applying the base
plugin—either directly or via another plugin such as the Java or Kotlin plugin—no longer realizes all configurations declared with register
or the incubating role-based factory methods (like configurations.resolvable(...)
).
This change can lead to reduced configuration time and lower memory usage in some builds.
To take advantage of this improvement, make sure to use the register
method over create
when declaring configurations:
configurations {
// Eager: this configuration is realized immediately
create("myEagerConfiguration")
// Lazy: this configuration is only realized when needed
register("myLazyConfiguration")
}
Gradle 8.13 introduced support for additional data in the Problems API, allowing users to attach extra context to reported problems—albeit with some limitations.
This release removes these limitations. You can now include any arbitrary data in problem reports.
This enhancement is especially valuable for IDE implementors managing both the plugin and its integration via the Tooling API, where conveying rich, structured diagnostics is critical.
For example, a custom worker task can report a problem and attach detailed additional data—including primitive fields, lists, and composed objects:
public abstract class ProblemWorkerTask implements WorkAction<ProblemsWorkerTaskParameter> {
// Use the Problems interface to report problems
@Inject
public abstract Problems getProblems();
// Use the ObjectFactory to create instances of classes for composition
@Inject
public abstract ObjectFactory getObjectFactory();
@Override
public void execute() {
ProblemId problemId = ProblemId.create("type", "label", ProblemGroup.create("generic", "Generic"));
getProblems().getReporter().report(problemId, problem -> problem
.additionalData(SomeData.class, dataInstance -> {
// Provider API properties can be used as arbitrary data
dataInstance.getSome().set("some");
// Getters and setters can be used as arbitrary data
dataInstance.setName("someData");
// Collections can be used as arbitrary data
dataInstance.setNames(Collections.singletonList("someMoreData"));
SomeOtherData compositionDataInstance = getObjectFactory().newInstance(SomeOtherData.class);
compositionDataInstance.setOtherName("otherName");
// Composition can be used as arbitrary data
dataInstance.setOtherData(compositionDataInstance);
})
);
}
}
The data attached to the problem is modeled with plain interfaces that use Gradle types where applicable:
import org.gradle.api.problems.AdditionalData;
import org.gradle.api.provider.Property;
import java.util.List;
public interface SomeData extends AdditionalData {
Property<String> getSome();
String getName();
void setName(String name);
List<String> getNames();
void setNames(List<String> names);
SomeOtherData getOtherData();
void setOtherData(SomeOtherData otherData);
}
public interface SomeOtherData {
String getOtherName();
void setOtherName(String name);
}
With this enhancement, the Problems API becomes a more powerful diagnostic tool—capable of carrying rich, structured, and typed context through the build, IDE, and Tooling API layers.
The new CustomAdditionalData.get()
method in the Tooling API (TAPI) allows consumers to retrieve additional data associated with build problems (or other events) reported during a Gradle build.
Previously, consumers of the Tooling API could only access a fixed set of predefined fields when inspecting problems or build events. With this new method, Gradle can serialize rich, structured data during the build and expose it to the Tooling API as a type-safe view interface.
On the receiving side, you can access this data like so:
void someMethod(List<Problem> problems) {
SomeDataView view = problems.get(0).getAdditionalData().get(SomeDataView.class);
System.out.println(view.getName());
System.out.println(view.getNames().get(0));
System.out.println(view.getOtherData().getOtherName());
}
These view interfaces mirror the structure of the data produced by the build logic:
interface SomeOtherDataView {
String getOtherName();
}
interface SomeDataView {
String getSome();
String getName();
List<String> getNames();
SomeOtherDataView getOtherData();
}
These types provide a safe and structured way to consume custom data in IDEs or other TAPI-based tools, without relying on brittle parsing or assumptions about internal data formats.
The configuration cache improves build time by caching the result of the configuration phase and reusing it for subsequent builds. This feature can significantly improve build performance.
To help diagnose obscure configuration cache loading errors, you can now enable stricter integrity checks using the org.gradle.configuration-cache.integrity-check
property.
This mode provides more detailed error messages to pinpoint the exact part of your build that failed to serialize correctly.
For example, instead of seeing a cryptic error like:
Index 4 out of bounds for length 3
You might now see:
Configuration cache state could not be cached: field `user` of task `:greet` of type `GreetTask`: The value cannot be decoded properly with 'JavaObjectSerializationCodec'. It may have been written incorrectly or its data is corrupted.
Note: Enabling integrity checks increases the size of the configuration cache and slows down cache reads/writes. Use it only for troubleshooting—not in regular builds.
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.