skip to content

What does @PathSensitive do on a task input, and why does it affect up-to-date checks and the build cache?

level: middleimportance: must knowfreq 55%

answer

  1. fingerprint = content + path bits
  2. RELATIVE = portable cache
  3. NONE ignores path
  4. ABSOLUTE busts cross-machine cache
  5. loosest correct sensitivity

basics

~10 s

@PathSensitive tells Gradle which part of a file's path counts when fingerprinting an input. With RELATIVE, only the relative path plus content matters, so moving the project directory still hits the cache.

solid answer

~40 s

Gradle decides whether a task is up-to-date (or has a cache hit) by fingerprinting its inputs — hashing both file contents and, depending on the chosen path sensitivity, parts of their paths. `@PathSensitive` on a `@InputFiles`/`@InputDirectory` property controls how much of the path participates. `ABSOLUTE` means the full path matters (relocating the workspace busts the cache). `RELATIVE` keeps only the path relative to the input root, so the same tree builds identically on another machine or directory — this is what makes the build cache shareable across agents. `NAME_ONLY` keeps just the filename; `NONE` ignores the path entirely and fingerprints content only. Choosing the loosest sensitivity that is still semantically correct maximizes up-to-date and cache hits without producing wrong results.

code

kotlin · 8 lines
kotlin
abstract class GenerateTask : DefaultTask() {
    @get:InputFiles
    @get:PathSensitive(PathSensitivity.RELATIVE)
    abstract val templates: ConfigurableFileCollection

    @get:OutputDirectory
    abstract val out: DirectoryProperty
}

go deeper

for a junior

Know that @PathSensitive controls how file paths are compared for up-to-date checks and that RELATIVE makes the cache portable.

for a middle

Explain the four modes, why RELATIVE is the default for portability, and that it is required on cacheable file inputs.

for a senior

Reason about when NONE is correct (content-only consumption) vs RELATIVE, and the cache-hit trade-offs of loosening sensitivity.

for a principal

Set org-wide guidance: default RELATIVE, audit ABSOLUTE usages that block shared remote cache across agents, and bake validation into the convention plugin.

## What a fingerprint is Gradle never re-runs a task if its inputs and outputs are unchanged. To decide that, it builds a **fingerprint** of every input property: for file inputs it hashes file content and, optionally, path information. If the combined fingerprint matches the previous run, the task is **UP-TO-DATE**; if it matches an entry in the **build cache**, Gradle pulls the outputs instead of executing. So a fingerprint is the cache key contribution of one input. ## Why the path matters For most tools the *content* of the files is what determines the output, but sometimes the *location* matters too (a compiler embeds relative paths into output, for example). `@PathSensitive(PathSensitivity)` declares how much of the path is part of the fingerprint: - `ABSOLUTE` — full absolute path participates. Building the same code in `/home/a/proj` vs `/home/b/proj` yields different fingerprints, so the cache can't be shared. Almost never what you want. - `RELATIVE` — only the path relative to the input root is hashed, plus content. Same logical tree → same fingerprint anywhere. The common, portable choice. - `NAME_ONLY` — only the file *name* (not directories) plus content. - `NONE` — path is ignored entirely; only content is hashed. Use when the file is consumed purely by content (e.g. a properties file read as a blob). ## How to declare it Path sensitivity is mandatory for `@InputFiles`/`@InputDirectory` on a `@CacheableTask` — Gradle warns if you omit it. Apply it next to the input getter. ```kotlin abstract class StampTask : DefaultTask() { @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) abstract val sources: ConfigurableFileCollection @get:OutputDirectory abstract val outputDir: DirectoryProperty @TaskAction fun run() { /* ... */ } } ``` ## Rule of thumb Pick the **loosest** sensitivity that is still correct. Looser = more inputs hash equal = more cache/up-to-date hits. `RELATIVE` is the safe default; drop to `NONE` only when paths genuinely don't affect the output; use `ABSOLUTE` essentially never.

  • Why is RELATIVE usually preferred over ABSOLUTE for a cacheable task?
    ABSOLUTE bakes the workspace path into the fingerprint, so two checkouts in different directories or two CI agents never share cache. RELATIVE strips the absolute prefix, making the fingerprint portable.
  • What happens if you forget @PathSensitive on a cacheable task's file input?
    Gradle emits a validation warning and falls back to ABSOLUTE path sensitivity, which silently kills cross-location cache hits. The build still works but caching is degraded.

Think of a fingerprint as a label on a moving box. ABSOLUTE writes the full street address — move the warehouse and nothing matches. RELATIVE writes only the shelf position inside the warehouse, so the box is recognized in any warehouse laid out the same way.

saying these in an interview costs you the question

  • Saying @PathSensitive changes which files are inputs — it only changes how their paths are fingerprinted, not the set of files.
  • Claiming ABSOLUTE is the safe default — it is the most restrictive and worst for caching.
  • Confusing path sensitivity with @Incremental / InputChanges — that is a different mechanism.

context