How exactly do missing or incorrect input/output declarations break Gradle's up-to-date checks and build cache?
answer
- Gradle sees only declarations
- under-declared input = stale (correctness)
- under-declared output = cache can't store
- over-declared/volatile = false misses
- --info shows the reason
basics
~20 sGradle decides up-to-date and the cache key from DECLARED inputs/outputs only. A missing input means changes go undetected (stale results); a missing output means the cache can't store/restore it, and over-declaring causes needless re-runs and cache misses.
solid answer
~50 sGradle's incremental engine knows nothing about what a task actually touches — only what you declare. **Under-declared inputs** are the dangerous case: a file the task reads but didn't declare won't change the fingerprint, so Gradle reports UP-TO-DATE and serves a stale output (or a stale cache entry, since the cache key is built from the same fingerprint). **Under-declared outputs** mean Gradle can't snapshot/restore that file, so the build cache won't store or relocate it and `clean` won't track it. **Over-declared inputs** (e.g. absolute paths, timestamps, machine-specific values) hurt the other way: they make the fingerprint differ across runs/machines, causing false cache misses and constant re-execution. The fix is to declare exactly the real inputs and outputs — no more, no less — and to normalize volatile ones (path sensitivity, ordering) so identical work yields identical keys.
code
bash · 4 lines# Ask Gradle WHY a task executed instead of being up-to-date
./gradlew bundle --info | grep -i "bundle"
# Debug how cache keys were computed
./gradlew bundle -Dorg.gradle.caching.debug=true --build-cachego deeper
State that Gradle only tracks declared inputs/outputs and a missing input means changes aren't noticed.
Distinguish the three failure modes (under-input, under-output, over-declared) and tie each to correctness vs performance.
Connect the input fingerprint to BOTH up-to-date and the cache key, and reason about volatile inputs needing normalization.
Discuss how silent staleness erodes trust in a CI cache and how to systematically detect/prevent it across teams.
## The core principle: Gradle only sees what you declare Gradle does not sandbox tasks or trace their syscalls. Its work-avoidance is built **entirely** on the inputs and outputs you declare through `inputs.file()/dir()/property()` and `outputs.file()/dir()`. From those declarations it computes a single **input fingerprint** (content hashes of input files + values of input properties). That fingerprint drives two features at once: 1. **Up-to-date checking** — re-run the task only if the fingerprint changed or a declared output is missing/altered. 2. **The build cache** — the fingerprint *is* the cache key; a hit lets Gradle restore the declared outputs from a local or remote cache instead of executing. ## Failure mode 1 — under-declared inputs (correctness bug) If a task reads a file (or an env value, or a system property) you didn't declare, that data is invisible to the fingerprint. Change it, and the fingerprint is unchanged, so Gradle: - reports the task `UP-TO-DATE` and skips it → **stale output**, or - finds a matching cache key and restores an output that was built under different conditions → **stale cache hit**. This is the worst kind of bug because it is silent and intermittent. ## Failure mode 2 — under-declared outputs (cache/relocation bug) If a task writes a file not declared as an output: - the build cache can't pack it into the cache entry, so a later cache hit restores an incomplete result; - Gradle can't relocate/clean it; stale files linger across builds. ## Failure mode 3 — over-declared / volatile inputs (performance bug) If you feed the fingerprint values that change even when the *meaningful* work doesn't — absolute file paths, timestamps, a full classpath in wrong order, build directory location — every run produces a different key. Result: tasks that should be `UP-TO-DATE` or cache hits instead re-run, and cross-machine cache sharing never hits. This is why **path sensitivity** and **classpath normalization** exist (handled by sibling topics): they strip volatile aspects so equivalent inputs hash equally. ## Diagnosing it Run with `--info` to see the *reason* a task ran ("Input property 'x' has changed", "No history is available"). Use the build scan or `-Dorg.gradle.caching.debug=true` to see how inputs were fingerprinted and why a cache key differed. The `validatePlugins` task / runtime validation flags some missing-declaration problems for typed tasks. ```kotlin tasks.register("bundle") { inputs.files(fileTree("src/assets")) // declare what we read inputs.property("minify", project.hasProperty("min")) outputs.file(layout.buildDirectory.file("bundle.js")) // declare what we write outputs.cacheIf { true } // opt this ad-hoc task into the cache doLast { /* read assets, honor minify flag, write bundle.js */ } } ``` ## The discipline Declare **exactly** the real inputs and outputs. Missing one breaks correctness; declaring a volatile one breaks performance. Normalize the ones that are logically stable but physically volatile.
- Why is an under-declared input worse than an over-declared one?An under-declared input is a silent correctness bug: changes go undetected and Gradle serves stale results. An over-declared input is only a performance bug: it causes extra (but correct) re-runs and cache misses. Wrong results are far costlier than slow ones.
- How does an undeclared output specifically harm the cache?The cache entry is built only from declared outputs, so an undeclared produced file isn't packed in. A later cache hit then restores an incomplete result — the missing file simply isn't there — which can break downstream tasks.
saying these in an interview costs you the question
- Claiming Gradle detects reads/writes automatically and declarations are optional optimization.
- Saying over-declaring is harmless — it causes false cache misses and constant re-execution.
- Treating a stale cache hit as 'the cache is broken' rather than a missing input declaration.