What does doNotTrackState() do, when would you use it, and what are the consequences?
answer
- marks task untracked
- always runs, never cached
- needs a reason string
- for non-reproducible side effects
- not a fix for churning inputs
basics
~10 sdoNotTrackState() tells Gradle a task has no reliable up-to-date state, so Gradle skips snapshotting its inputs/outputs and always runs it. The cost: the task never shows UP-TO-DATE and is never cached.
solid answer
~50 s`doNotTrackState("reason")` is a `Task` method that opts the task out of input/output state tracking. Gradle no longer fingerprints its inputs or outputs, so up-to-date checking can't conclude the task is current — it executes on every invocation and is excluded from the build cache. You use it when a task's behavior is genuinely non-deterministic or has side effects Gradle can't model: probing a live external service, talking to a daemon with mutable state, or producing output that's never reproducible. It's the honest declaration "I have no stable state to compare." The trade-off is losing all incremental benefit for that task, so it should be a narrow, last resort — preferred over silently lying with `@Internal` on a real input, but inferior to making the task deterministic. The string argument documents *why*, which surfaces in build scans and validation.
code
kotlin · 4 linestasks.register("probeEnvironment") {
doNotTrackState("Reads live host state; not reproducible")
doLast { println("always executes") }
}go deeper
Know that doNotTrackState makes a task always run and never cache.
Explain the trade-off and a valid use case (non-reproducible external work).
Contrast it with @Internal and upToDateWhen{false}, and judge when it's the wrong tool.
Treat each usage as an incrementality hole to audit; set policy that requires justification and review for untracked tasks.
## What it is `Task.doNotTrackState(reason: String)` marks a task as **untracked**. Normally Gradle snapshots a task's declared inputs and outputs to decide UP-TO-DATE and to enable caching. An untracked task skips that snapshotting entirely. ## What changes when you call it - The task **always executes** — it can never be reported `UP-TO-DATE`. - The task is **never stored in or restored from the build cache** (no input key to compute reliably). - Gradle relaxes some output-overlap validations, because it no longer owns the task's output state. - The supplied reason string is recorded and shown in diagnostics (e.g., build scans). ```kotlin tasks.register("refreshFromLiveApi") { doNotTrackState("Fetches volatile data from a live endpoint; output is never reproducible") doLast { // always run; result depends on external mutable state } } ``` ## When it's appropriate 1. **Non-reproducible side effects** — a task that queries a live service, current network state, or wall-clock-dependent external system. 2. **Tasks that mutate shared/external state** Gradle can't fingerprint (e.g., touching a directory another tool also writes). 3. **Legacy/opaque tooling** where you cannot enumerate the real inputs/outputs accurately. ## When it's the WRONG tool - To silence up-to-date churn from a *fixable* non-deterministic input (a baked-in timestamp). Make the input deterministic instead. - As a substitute for declaring inputs/outputs. `@Internal` and proper declarations are correct for trackable tasks; `doNotTrackState` is for genuinely untrackable ones. ## Relationship to other opt-outs - `@Internal` excludes a single *property* from tracking but the task is still up-to-date-checked on its other properties. - `doNotTrackState()` excludes the *whole task* from up-to-date checking and caching. - `outputs.upToDateWhen { false }` forces a task out of date based on a predicate but still tracks state; `doNotTrackState` goes further by skipping snapshotting altogether. ## The mental model Think of it as the task author admitting: "My result has no stable fingerprint, so don't pretend it does." That honesty keeps builds correct (no stale outputs) at the cost of always paying for the task. Audit its usage — every `doNotTrackState` is a permanent hole in incrementality.
- How is doNotTrackState() different from outputs.upToDateWhen { false }?upToDateWhen { false } still snapshots inputs/outputs but forces the task out of date via a predicate; doNotTrackState skips snapshotting entirely and also disables caching. The latter is the right choice when there's genuinely no reliable state.
- Does an untracked task break tasks that depend on its output?No — dependents still wire to its outputs and run after it. But because the untracked task always runs, dependents may also re-run more often, so apply it narrowly.
- Why does it require a reason string?To document the justification; the reason surfaces in build scans and reviews so the deliberate loss of incrementality is visible and auditable.
saying these in an interview costs you the question
- Using it to mask a non-deterministic input you could make stable.
- Claiming an untracked task can still be FROM-CACHE — it can't.
- Confusing it with @Internal, which only excludes one property.