skip to content

A teammate complains that a task keeps re-running even though 'nothing changed.' From a consumer's seat, how do you find out why Gradle decided the task was no longer up-to-date?

level: middleimportance: must knowfreq 68%

answer

  1. --info (or -i) prints the reason
  2. 'is not up-to-date because: ...'
  3. input property vs input file vs output removed
  4. 'No history is available' on first run
  5. --scan / --debug for deeper digs

basics

~10 s

Run the build with --info. For each non-skipped task Gradle prints a reason like 'Input property X has changed' or 'Output file Y has been removed', telling you exactly why it re-executed.

solid answer

~40 s

Re-run the build with `--info` (or `-i`). For every task that is **not** UP-TO-DATE, Gradle logs the reason it ran, e.g.: - `Task ':compileJava' is not up-to-date because: Input property 'options...' has changed` - `...Output property 'destinationDirectory' file X has been removed.` - `...No history is available.` (first run, or build directory wiped) That message points straight at the culprit — a changed input property, a modified/added/removed source file, a deleted or touched output, or missing history. Common 'phantom' causes: a timestamped/generated file landing in an input directory, an output being modified by another tool, an absolute path or environment value leaking into an input property, or running with `--rerun-tasks`. `--info` turns the up-to-date decision from a black box into a one-line explanation you can act on.

code

bash · 8 lines
bash
$ ./gradlew compileJava --info
...
> Task :compileJava
Task ':compileJava' is not up-to-date because:
  Input property 'options.compilerArgs' has changed.
  - new value: [-parameters]
  - old value: []
...

go deeper

for a junior

Know that --info exists and that it prints a human-readable 'not up-to-date because' reason.

for a middle

Map each reason category (input property/file vs output removed vs no history) to a concrete cause and fix.

for a senior

Use --info/--scan to triage flaky incrementality (env-sensitive inputs, polluted input dirs) and decide what to fix.

for a principal

Establish team practices: build scans in CI, guidance on environment-stable inputs, and dashboards for non-incremental tasks.

## Diagnosing why a task re-ran Gradle's up-to-date check is normally silent — it just shows `UP-TO-DATE` or nothing. To see the **reason** behind the decision, raise the log level. ### The tool: `--info` Run: ```bash ./gradlew <task> --info ``` For each task that executes, Gradle prints a line beginning: ```text > Task :compileJava Task ':compileJava' is not up-to-date because: Input property 'options.compilerArgs' has changed. ``` The phrasing names the **category** of change: | Message | What changed | |---|---| | `Input property 'X' has changed.` | A scalar input property (flag, version, arg) differs. | | `Input file/directory X has changed.` / `...has been added/removed.` | A source file in a declared input set was modified/added/removed. | | `Output property 'X' file ... has been removed.` / `...has changed.` | A declared output was deleted or modified outside the task. | | `No history is available.` | First run, or `.gradle`/build dir was wiped — nothing to compare against. | ### Reading it as a consumer You don't need to know how the fingerprint is computed; you just read the line and map it back to something concrete: - *Input property changed* → did the Gradle/JDK version, a compiler arg, or a project property change? CI vs local often differ here. - *Input file added/removed* → a generated or temp file is polluting an input directory. - *Output removed/changed* → something (an IDE, another task, a cleanup script) is touching the outputs between runs. - *No history available* → expected after `clean` of the `.gradle` metadata or on a fresh checkout. ### Going deeper If `--info` isn't enough, `--debug` shows even more, and a **Build Scan** (`--scan`) presents per-task up-to-date reasons in a web UI — useful when comparing two builds that 'should' be identical. But `--info` is the everyday first stop. ### Watch-outs - `--rerun-tasks` forces everything to run; if a colleague's alias includes it, every task will look non-incremental. (Forcing reruns is its own topic.) - Environment-sensitive inputs (absolute paths, hostnames, timestamps embedded in resources) cause a task to re-run on every machine — `--info` will flag the offending input property.

  • You see 'No history is available' on a CI agent every build. Why, and is it a problem?
    The agent starts from a clean workspace each run, so there's no prior local state to compare. It's expected; the build cache (FROM-CACHE) is what restores incrementality across fresh CI workspaces, not local up-to-date history.
  • If --info says 'Input file X has been added', what's a likely root cause?
    A generated, temp, or editor file is being written into a directory that's declared as a task input. Excluding it from the input set (or stopping it from being created there) restores up-to-date behaviour.

Like a 'why did this email go to spam?' explainer — instead of just silently filtering, the system tells you which rule fired so you can fix the cause.

saying these in an interview costs you the question

  • Suggesting you must read Gradle source or hashing internals to know why a task reran — --info already states the reason.
  • Assuming 'nothing changed' is literally true; --info usually reveals a real input/output difference.
  • Reaching for --rerun-tasks (which forces reruns) instead of --info (which explains them).

context