skip to content

What does markTaskAsNotCompatibleWithConfigurationCache() (or notCompatibleWithConfigurationCache()) do, and when is it the right tool? What are its downsides?

level: seniorimportance: should knowfreq 35%

answer

  1. notCompatibleWithConfigurationCache(reason)
  2. mandatory reason string shown in report
  3. disables cache reuse for invocations including the task
  4. doesn't fail the build for that task
  5. stopgap, not a fix — audit and migrate off

basics

~20 s

It marks a specific task as incompatible with the configuration cache, telling Gradle to skip reusing the cached config when that task runs (and not report it as a hard failure). It's a stopgap for tasks you can't fix yet; the downside is you lose cache benefit for any build including that task.

solid answer

~50 s

`Task.notCompatibleWithConfigurationCache("reason")` (the public DSL; `markTaskAsNotCompatibleWithConfigurationCache` is the older/internal-style name) declares that a given task can't run under the configuration cache. Gradle then **degrades gracefully for builds that include that task**: it won't fail the build over that task's problems, and it effectively disables config-cache reuse for invocations requesting it, while still reporting the task as incompatible in the report. It's the right tool when you don't own the task (third-party plugin) or can't migrate it immediately but need green builds. Downsides: any build that runs the marked task gets **no configuration-cache benefit** for that run, so it's a per-task escape hatch, not a fix; it can mask the real problem; and if the task is on a common path you lose the cache everywhere it's invoked. Treat it as a temporary marker with a tracking item, not a destination.

code

kotlin · 5 lines
kotlin
tasks.named("thirdPartyTask") {
    notCompatibleWithConfigurationCache(
        "Plugin reads Project at execution time; tracked in BUILD-1234"
    )
}

go deeper

for a junior

Know it opts a single task out of the configuration cache and needs a reason; it's a temporary measure.

for a middle

Explain that builds including the task lose reuse but it stops the build failing, and that it's per-task not per-problem.

for a senior

Discuss when it's justified (unowned/third-party tasks), the cost on common paths, and that it masks fixable issues.

for a principal

Set governance: require tickets for each marker, audit the report regularly, drive plugin owners to remove the need, and weigh common-path cache loss across the org.

## The API Gradle exposes, on `Task`, a method to declare a task incompatible with the configuration cache: ```kotlin tasks.register("legacyThing") { notCompatibleWithConfigurationCache("Reads project state at execution time; pending migration") doLast { /* ... */ } } ``` The reason string is mandatory and shows up in the report. (`markTaskAsNotCompatibleWithConfigurationCache(...)` is the older naming you may see referenced; the modern public DSL is `notCompatibleWithConfigurationCache`.) ## What it actually does - For any build invocation that **includes the marked task**, Gradle treats that task's configuration-cache problems as **expected**, so they don't fail the build even in `fail` mode. - Practically, including such a task **disables configuration-cache reuse for that invocation** — Gradle won't serve a stored entry, because the task can't be safely replayed from the cache. So you keep correctness at the cost of the speedup for those runs. - The task still appears in the report, flagged as incompatible with your reason, so it stays visible. ## When it's the right tool - **Third-party / core tasks you don't own** that aren't yet migrated. - **Genuinely incompatible work** that can't reasonably be made cacheable soon (rare). - As a **bridge** so the rest of the build can adopt the configuration cache now, with this one task explicitly carved out. ## Downsides and cautions - It's an **escape hatch, not a fix**: the task gains nothing and any build path through it loses cache reuse. - It can **hide a fixable problem** — easy to mark and forget. - If the task sits on the **default/common build path**, you may quietly lose configuration-cache benefit across the whole team. - It's **per task**, not per problem — you can't selectively allow some of a task's problems. ## Good practice Use it sparingly, always with a meaningful reason and an associated tracking ticket; periodically audit the report for these markers and migrate them off. Prefer the real fix — `Provider`/`Property`, service injection, `providers.*` for inputs — wherever you own the code.

  • If I mark one task incompatible, does the rest of the build still benefit from the configuration cache?
    Builds that don't include that task still get full cache reuse. But any invocation that does include the marked task loses configuration-cache reuse for that run, since the task can't be replayed from the cache.
  • Is marking a task incompatible the same as turning off the configuration cache globally?
    No. Disabling the cache (org.gradle.configuration-cache=false) affects every build. Marking a task is scoped: only invocations including that task forgo reuse, and the cache still works for everything else.
  • Why does Gradle require a reason string?
    So the report documents why the task is carved out, making the escape hatch visible and auditable rather than silent.

saying these in an interview costs you the question

  • Treating the marker as a permanent solution rather than a tracked stopgap.
  • Believing it makes the task cacheable — it does the opposite, it opts the task out.
  • Confusing it with @CacheableTask (build/output cache) — different mechanism entirely.

context