A task keeps producing stale results even after you change a source file. How do you confirm and fix a missing input declaration?
answer
- stale output = missing input declaration
- --info shows up-to-date reason
- --scan / caching.debug lists fingerprinted inputs
- declare file/dir/property
- watch hidden env/system-property inputs
basics
~10 sRun with --info to see Gradle report the task UP-TO-DATE despite your change — that confirms the changed file isn't a declared input. Add it via inputs.file()/dir()/property() so its content joins the fingerprint.
solid answer
~50 sStale output after editing a source file almost always means that file isn't a **declared input**, so it never enters the task's fingerprint. **Confirm**: run `./gradlew <task> --info` and look for the up-to-date reason — if it still says UP-TO-DATE (or gives no change reason) after your edit, the input is missing. A build scan or `-Dorg.gradle.caching.debug=true` shows exactly which inputs were fingerprinted. **Fix**: declare the real input — `inputs.file(...)` for a single file, `inputs.dir(...)` for a tree, or `inputs.property(name, value)` for a scalar like an env var or system property the task reads. After adding it, the changed content alters the fingerprint, so Gradle re-runs and (if cached) invalidates the stale cache entry. The same applies to outputs: a file the task writes but doesn't declare won't be tracked or restored from cache, so declare it with `outputs.file()/dir()`.
code
bash · 3 lines./gradlew render --info # shows 'up-to-date' even after edit => missing input
./gradlew render --scan # lists which inputs Gradle fingerprinted
# After adding inputs.dir("src/pages"), re-run: task now executes on changego deeper
Know to run with --info and that a still-up-to-date task after an edit signals a missing input to declare.
Use --scan/caching.debug to inspect the fingerprint and pick the right inputs.file/dir/property fix.
Reason about hidden non-file inputs (env/system property/clock) and when a task simply shouldn't be cacheable.
Establish diagnostics/validation practices so under-declared tasks are caught before they poison a shared cache.
## Symptom → cause "I changed X but the task didn't re-run / gave old output" is the textbook signature of an **under-declared input**. Gradle's fingerprint is built only from declared inputs; an undeclared file/value is invisible, so the fingerprint doesn't change and Gradle skips the task (or, with caching, restores a stale entry). ## Step 1 — confirm with --info ```bash ./gradlew myTask --info ``` Gradle prints a reason for each task's state: - `Task ':myTask' is up-to-date` (after you changed a file) → the change is invisible → missing input. - `Input property 'foo' has changed for task ':myTask'` → that input IS tracked. - `No history is available` → first run, nothing to compare. If editing the file does **not** flip the task off up-to-date, the file isn't declared. ## Step 2 — inspect the fingerprint A **build scan** (`--scan`) lists the task's inputs and whether each changed. Alternatively `-Dorg.gradle.caching.debug=true --build-cache` logs how the cache key was computed, including each input file's hash. If your file isn't in that list, it's undeclared. ## Step 3 — declare the missing input Match the kind of data: - a single file the task reads → `inputs.file(path)` - a directory/tree it reads → `inputs.dir(path)` or `inputs.files(fileTree(path))` - a scalar it branches on (env var, system property, flag, version) → `inputs.property("name", value)` ```kotlin tasks.register("render") { inputs.dir("src/pages") // was missing → caused stale output inputs.property("theme", System.getenv("THEME")) // env the task reads outputs.dir(layout.buildDirectory.dir("site")) doLast { /* read src/pages, honor THEME, write build/site */ } } ``` ## Hidden non-file inputs The trap is non-file inputs: environment variables, system properties, the system clock, random seeds, or network responses. These can't be hashed as files; declare the meaningful ones as `inputs.property(...)`. Truly non-deterministic inputs (clock, network) mean the task shouldn't be cacheable at all — use `outputs.doNotCacheIf { ... }` or don't opt it in. ## Don't forget outputs If the *output* file is what's stale/missing after a cache hit, the produced file wasn't declared with `outputs.file()/dir()`, so the cache didn't store/restore it. Declare it. ## Verify the fix Re-run, edit the file again, and confirm `--info` now reports the task executing with a change reason. If caching is on, confirm the cache key changes between the two contents.
- How would you handle a task whose behavior depends on an environment variable?Declare it as inputs.property("VAR", System.getenv("VAR")) so its value joins the fingerprint and a change re-triggers the task. Better still, read it via providers.environmentVariable("VAR") so it resolves lazily and is configuration-cache friendly.
- What if the stale data comes from a network call inside the task?Network responses are non-deterministic and unhashable, so the task can't be safely cached or made up-to-date on them. Either don't cache it (no cacheIf / use doNotCacheIf), or restructure so the fetched data is materialized into a declared input file by a separate step.
saying these in an interview costs you the question
- Blaming 'a Gradle bug' or deleting the build dir as the fix instead of declaring the missing input.
- Disabling caching globally to mask a single under-declared task.
- Assuming environment/system-property reads are tracked automatically.