skip to content

Why must a @CacheableTask declare path sensitivity (e.g. @PathSensitive(RELATIVE)) on its file inputs, and what goes wrong if you leave the default ABSOLUTE sensitivity?

level: middleimportance: must knowfreq 55%

answer

  1. key hashes content + path
  2. ABSOLUTE = per-checkout key
  3. RELATIVE for sources
  4. NONE / NAME_ONLY looser
  5. validatePlugins warns

basics

~10 s

Absolute paths differ per machine/checkout, so absolute path sensitivity makes the cache key machine-specific and kills cross-machine hits. Cacheable tasks should use RELATIVE (or NAME_ONLY/NONE) so the key reflects content, not location.

solid answer

~40 s

A cache key is a hash of a task's inputs, and for file inputs Gradle hashes both the file **contents** and, by default, their **absolute paths**. Two developers (or CI agents) check the project out under different directories, so identical files have different absolute paths — the keys diverge and you get *zero* remote cache hits. Declaring `@PathSensitive(PathSensitivity.RELATIVE)` makes Gradle hash the path **relative to the input root** instead, so `src/main/java/Foo.java` hashes the same everywhere. That's why Gradle *warns* (and, for `validatePlugins`, can fail) when a `@CacheableTask` has file inputs without explicit path sensitivity. Choose the loosest sensitivity that's still correct: `NONE` (content only), `NAME_ONLY`, `RELATIVE`, or `ABSOLUTE` — looser = more hits but only valid if the path truly doesn't affect output.

code

kotlin · 12 lines
kotlin
@CacheableTask
abstract class Bundle : DefaultTask() {
    @get:InputFiles
    @get:PathSensitive(PathSensitivity.RELATIVE)
    abstract val sources: ConfigurableFileCollection

    @get:Classpath
    abstract val runtime: ConfigurableFileCollection

    @get:OutputDirectory
    abstract val outDir: DirectoryProperty
}

go deeper

for a junior

Know that cacheable file inputs need @PathSensitive and RELATIVE is the common choice.

for a middle

Explain that the cache key hashes path + content, why ABSOLUTE breaks cross-machine hits, and the mode hierarchy.

for a senior

Reason about choosing the loosest correct mode and using @Classpath/@CompileClasspath for classpath inputs.

for a principal

Tie path normalization choices to remote-cache hit rates and a validatePlugins-gated convention across many plugins.

## The cache key, briefly For each task Gradle builds a **cache key** by hashing: the task type/classpath, input properties (`@Input` values), and the **fingerprint of file inputs** (`@InputFiles`, `@InputFile`, `@InputDirectory`, `@Classpath`). The file fingerprint includes file *content hashes* and *path information*. ## Why absolute paths poison cross-machine caching By default a file input's path is treated as **absolute**. Absolute paths embed the checkout location — `/home/ci/agent-3/work/proj/src/...` vs `/Users/dev/proj/src/...`. Same bytes, different path ⇒ different fingerprint ⇒ different key ⇒ a remote-cache **miss** between machines. Cross-machine reuse (the main payoff of a remote cache) collapses. ## Path sensitivity modes `@PathSensitive(PathSensitivity.X)` controls what part of the path enters the fingerprint: - **`ABSOLUTE`** (default): full path. Rarely correct for caching. - **`RELATIVE`**: path relative to the input root. The usual choice for sources — `Foo.java`'s package-relative path matters but the checkout root doesn't. - **`NAME_ONLY`**: only the file name, not directories. - **`NONE`**: contents only; location ignored entirely (e.g. a bag of jars where order/location is irrelevant). ## Why Gradle nags you Gradle treats missing path sensitivity on a `@CacheableTask`'s file inputs as a validation problem: `validatePlugins` reports it, and at runtime you may see a deprecation/validation warning. The framework wants you to *consciously* pick a mode, because silently using ABSOLUTE produces a technically-correct-but-useless cache. ```kotlin @CacheableTask abstract class Bundle : DefaultTask() { @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) // hashes src-relative paths abstract val sources: ConfigurableFileCollection @get:Classpath // order-sensitive, ignores paths abstract val runtime: ConfigurableFileCollection @get:OutputDirectory abstract val outDir: DirectoryProperty } ``` ## Related normalizations `@Classpath`/`@CompileClasspath` are specialized normalizers: they ignore absolute paths *and* apply classpath-aware rules (order matters, but jar entry timestamps and irrelevant resources can be ignored). They're the right annotation for classpath inputs rather than plain `@InputFiles` + path sensitivity.

  • When is @PathSensitive(NONE) appropriate?
    When only file contents affect the output and the location/name is irrelevant — e.g. concatenating a set of files where neither directory nor filename influences the result.
  • Should you use @PathSensitive on a classpath input?
    No — use @Classpath (or @CompileClasspath). They already ignore absolute paths and apply classpath-aware normalization (order matters; jar timestamps/irrelevant entries ignored).
  • What tool surfaces a missing path-sensitivity declaration?
    The validatePlugins task (and runtime validation warnings) flags @CacheableTask file inputs lacking explicit path sensitivity.

saying these in an interview costs you the question

  • Leaving default ABSOLUTE on a cacheable task's inputs and expecting CI cache hits.
  • Using @InputFiles + @PathSensitive for classpath inputs instead of @Classpath.
  • Picking NONE just for more hits when the path genuinely affects output.

context