Diagnosing Gradle Build Cache Misses When Task Outputs Are Not Reused
Gradle build cache enabled but tasks still run from scratch? Diagnose cache misses by checking activation, task annotations, input stability, and remote connectivity.
22 Feb 2026, 04:29 UTC

Recognizable condition
You have enabled the Gradle build cache in settings.gradle (or settings.gradle.kts) and you pass --build-cache on the command line, yet tasks such as compileJava or test execute from scratch on every invocation. The console shows > Task :compileJava with no FROM-CACHE marker, and a build scan reports zero cache hits. The build cache is designed to store task outputs locally or on a remote HTTP server and restore them when the declared inputs match a previous execution. When that reuse never occurs, the cause is usually one of four things: the cache is not active, the task is not cacheable, the cache key changes unexpectedly, or the remote endpoint is unreachable.
Cause and diagnostic table
| Condition | Likely cause | Quick diagnostic |
|---|---|---|
No task ever shows FROM-CACHE | Cache not enabled in settings, or --build-cache flag omitted | Run gradle clean build --build-cache --info | grep -i cache and look for cache-related log lines |
| Built-in tasks cache, custom tasks never do | Custom task missing @Cacheable annotation or incomplete input/output declarations | Inspect --info output for the specific task action; check source annotations |
| Cache hit occurs but outputs are wrong or stale | Non-deterministic task logic, or undeclared inputs that change between runs | Compare outputs from --build-cache and --no-build-cache runs |
| Remote cache errors or timeouts | Unreachable URL, missing credentials, or TLS misconfiguration | Review --info logs for HTTP status codes or connection exceptions |
Ordered checks
1. Confirm the cache is active
Run the following in your project root (the directory containing settings.gradle). You need the same permissions you use for a normal build.
gradle clean build --build-cache --info | grep -i cache
Expected check: you should see lines indicating the cache is being used, such as Build cache configuration messages or task actions reporting FROM-CACHE. If the output is silent, the cache is likely disabled. The local cache directory defaults to ~/.gradle/build-cache on Linux and macOS, or %USERPROFILE%\.gradle\build-cache on Windows. Verify that directory exists and is writable.
2. Verify task cacheability
Gradle only caches tasks that are explicitly marked cacheable. Built-in tasks like JavaCompile are usually cacheable by default, but custom tasks require the @Cacheable annotation and complete input/output annotations. For example, a Kotlin task needs:
abstract class GenerateSourcesTask : DefaultTask() {
@get:Input
abstract val version: Property<String>
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction
fun generate() { /* write files */ }
}
If @Cacheable is missing, the task will never be stored or retrieved, regardless of the global cache setting.
3. Inspect cache keys and inputs
The cache key is a hash of the task's declared inputs, outputs, and implementation. If an input changes on every run—such as a timestamp, a random value, or an absolute file path—the key changes and the cache misses. Run the build twice with identical inputs and compare the task action in --info logs. Identical inputs should produce a cache hit on the second run. If they do not, review the task's property annotations for undeclared dependencies.
4. Check remote cache connectivity
If you configured a remote cache in settings.gradle, confirm the URL is reachable and credentials are supplied. Remote caches typically require an HTTP server with the build-cache protocol, such as Gradle Enterprise or an Apache WebDAV endpoint. In the --info output, look for HTTP status codes or authentication failures. Do not hardcode credentials in the build script; supply them via environment variables or gradle.properties.
5. Measure with and without the cache
On Linux or macOS, use the shell built-in time:
time gradle clean build --build-cache
time gradle clean build --no-build-cache
On Windows PowerShell, use Measure-Command. A healthy cache should show a meaningful reduction in wall-clock time on the second clean build, because outputs are restored instead of regenerated. If the times are identical, the cache is not contributing.
Fixes tied to findings
Enable the local cache in settings
If check 1 shows the cache is inactive, add the local cache block to settings.gradle (Groovy DSL):
buildCache {
local {
enabled = true
}
}
For Kotlin DSL, use settings.gradle.kts with the same structure. The local cache is enabled by default in many Gradle distributions, but explicitly setting it removes ambiguity.
Make custom tasks cacheable
If check 2 reveals missing annotations, add @Cacheable to the task class and ensure every file or value read by the task action is annotated with @Input, @InputFile, @Classpath, or similar, and every produced artifact is annotated with @OutputFile or @OutputDirectory. Missing input annotations are a common cause of both cache misses and unsafe stale hits.
Stabilize non-deterministic inputs
If check 3 shows cache misses despite identical logical inputs, remove or normalize volatile values. Use @Input with a normalized property instead of embedding timestamps or absolute paths. For tasks that genuinely cannot be made deterministic, use cacheIf or cacheOtherwise directives to control caching behavior explicitly.
Configure remote cache securely
If check 4 shows connectivity or authentication errors, verify the URL scheme (HTTPS is strongly recommended) and supply credentials out-of-band:
buildCache {
remote(HttpBuildCache) {
url = 'https://cache.example.com/'
push = true
}
}
Set the username and password via environment variables or a gradle.properties file outside version control. An unauthenticated remote cache can expose build secrets and should not be used in shared environments.
Manage local cache disk usage
The local cache directory grows over time. Gradle performs automatic cleanup, but you should monitor disk usage. If the directory consumes excessive space, you can disable the local cache temporarily with --no-build-cache or clear the directory manually between major project changes.
Escalation criteria
Escalate to a build infrastructure engineer or cache administrator when:
- The remote cache consistently returns HTTP 5xx errors or authentication failures that are not resolved by credential rotation.
- Build scans show cache misses for tasks with identical inputs across multiple developer machines, suggesting a cache key normalization issue in the build logic itself.
- You need to enforce authentication, authorization, or retention policies on a shared remote cache.
- Cache corruption is suspected—for example, a cache hit produces outputs that differ from a fresh
--no-build-cacheexecution.
Limitations and verification
This guide assumes Gradle 7.x or newer. The settings DSL and local cache directory layout are stable in these versions, though the Kotlin DSL syntax differs from Groovy. The build cache improves incremental performance only when tasks are deterministic and their inputs are fully declared. Non-deterministic tasks will either miss the cache or, worse, produce incorrect cache hits if volatile inputs are not annotated. Cache keys are also sensitive to the Gradle version and JVM used to run the build; upgrading Gradle can invalidate existing cache entries.
To verify the result, run gradle clean build --build-cache --info | grep -i cache and confirm that expected tasks report FROM-CACHE. Then run gradle clean build --no-build-cache and compare the wall-clock time. A build scan generated with --scan provides per-task cache hit and miss statistics, which is the most reliable way to confirm that the cache is contributing to faster builds.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.