skip to content

A team enabled configuration on demand and now some tasks intermittently fail or aren't found. What's the likely cause and how would you diagnose it?

level: seniorimportance: should knowfreq 22%

answer

  1. cross-project config not honored
  2. task-not-found / missing deps, intermittent by task set
  3. subprojects{}/allprojects{} registering tasks
  4. diagnose with --info + --no-configure-on-demand A/B
  5. fix = convention plugins, self-contained config

basics

~20 s

The build probably relies on cross-project configuration (allprojects/subprojects blocks or one project reaching into another). Under configure-on-demand the project that applies that wiring may never be configured, so tasks it would create or modify go missing.

solid answer

~50 s

Configure-on-demand only configures projects reachable from the requested tasks via declared dependencies, so any wiring that lives in an **unconfigured** project — or that mutates a project from *outside* — may simply not run. Typical culprits: `subprojects {}`/`allprojects {}` blocks that register tasks or apply plugins, one build script doing `project(":other").tasks.register(...)`, or convention setup that isn't applied per-project. Symptoms are exactly 'task not found' or tasks missing inputs/dependencies, and they're **intermittent** because they depend on which task subset you requested (and thus which projects got configured). To diagnose: run with `--info` and compare the list of configured projects against where the missing task is supposed to be defined; reproduce by requesting only the narrow task. The fix is to make each project's configuration **self-contained** — move shared wiring into **convention plugins** applied within each project's own build script — after which configure-on-demand (and the configuration cache, and Project Isolation) all behave correctly.

code

kotlin · 9 lines
kotlin
// ANTI-PATTERN (root build.gradle.kts): breaks under configure-on-demand
subprojects {
    tasks.register("report") { /* ... */ } // may not exist when the project isn't configured
}

// FIX: convention plugin applied per-project (buildSrc/.../myteam.report-conventions.gradle.kts)
tasks.register("report") { /* ... */ }
// each subproject's own build.gradle.kts:
plugins { id("myteam.report-conventions") }

go deeper

for a junior

Recognize that cross-project configuration can break under configure-on-demand and that turning it off is a quick test.

for a middle

Explain why the failure is intermittent (depends on requested tasks) and how to diagnose with --info and an A/B run.

for a senior

Prescribe the convention-plugin refactor toward self-contained configuration and connect it to other features that need it.

for a principal

Set an org policy banning cross-project configuration so configure-on-demand, the configuration cache, and Project Isolation all stay correct at scale.

## The failure mode Configure-on-demand's correctness rests on one assumption: a project's contribution to the task graph is captured by **its own script plus its declared project dependencies**. When that assumption is violated, parts of the graph silently don't get built. Common violations: - **`subprojects {}` / `allprojects {}` in root** that *register or modify tasks* in child projects. If root is configured (it always is) but a child isn't selected, the block may still run against unconfigured children inconsistently, or the intended task wiring won't be visible where expected. - **Imperative cross-project access**: `project(":other").tasks.register("x")` in project A's script. If A isn't configured for the requested task set, `:other:x` never gets created. - **Order-dependent configuration**: logic that assumes another project was already configured. ## Why it's intermittent Which projects get configured depends on the **requested tasks**. `gradle :app:assemble` configures one reachable set; `gradle :app:check :reports:report` configures another. A task that's defined via cross-project wiring will exist in one invocation and vanish in another — classic 'works on my machine, fails in CI' flakiness, except the variable is the task selection, not the machine. ## Diagnosis checklist 1. Reproduce with the **narrowest** failing invocation (`gradle :app:thatTask`). 2. Run with `--info`; note exactly which projects Gradle reports configuring. 3. Check whether the missing/broken task is defined in a project **not** in that list, or via a cross-project block. 4. Temporarily run with `--no-configure-on-demand`; if it passes, you've confirmed the cause. ```bash gradle :app:thatTask --configure-on-demand --info | grep -i 'configuring project' gradle :app:thatTask --no-configure-on-demand # passes? -> cross-project config is the culprit ``` ## The durable fix Make configuration **self-contained**: - Extract shared logic into **convention plugins** (precompiled script plugins in `buildSrc` or an included build), and apply them inside each project's own `build.gradle(.kts)` (`plugins { id("myteam.java-conventions") }`). - Eliminate `allprojects {}`/`subprojects {}` that register or mutate tasks; replace with the plugin applied per project. - Never reach into `project(":other")` from another project's script. This is the same discipline the **configuration cache** and **Project Isolation** require, so the refactor pays off three times over. ## Why not just turn it off? You can (`--no-configure-on-demand`), and for a build heavily reliant on cross-project config that's the pragmatic short-term call. But the long-term answer is the refactor, because the same anti-patterns block the much larger wins from the configuration cache and Project Isolation.

  • Why does `--no-configure-on-demand` make the problem disappear?
    Without configure-on-demand, Gradle configures every project, so the cross-project wiring (and the tasks it creates) always runs — masking the underlying anti-pattern.
  • How do convention plugins solve the root cause?
    They move shared configuration into a plugin applied inside each project's own script, so a project's full configuration runs whenever that project is configured — no dependence on another project's script executing.
  • What other Gradle features benefit from the same self-contained-configuration refactor?
    The configuration cache and Project Isolation both require that projects don't mutate each other at configuration/execution time, so the same fix unlocks them.

saying these in an interview costs you the question

  • Blaming flaky hardware/CI rather than recognizing the task-set-dependent configuration.
  • Recommending only `allprojects {}` everywhere as the fix — that's the anti-pattern causing it.
  • Permanently leaving configure-on-demand off without addressing the cross-project coupling that also blocks the configuration cache.

context