skip to content

A teammate sets org.gradle.java.home to a JDK that doesn't meet Gradle's requirements (too old, or a JRE), and the build fails. How do you diagnose and resolve daemon-JVM-selection problems like this?

level: seniorimportance: should knowfreq 30%

answer

  1. ./gradlew --version shows the JVM
  2. JDK not JRE; supported range
  3. criteria > org.gradle.java.home > JAVA_HOME
  4. stale committed gradle.properties path
  5. ./gradlew --stop after changing

basics

~20 s

Check which JVM the daemon is actually using (./gradlew --version or buildEnvironment), verify it meets Gradle's supported range and is a full JDK, then fix the selection via org.gradle.java.home, the criteria file, or JAVA_HOME — and remove stale daemons.

solid answer

~40 s

Daemon-JVM problems usually surface as 'incompatible Java version', 'Java home ... does not exist', or a daemon that won't start. Diagnose by finding which JVM Gradle resolved: `./gradlew --version` prints the JVM in use, and `--info`/`--debug` logs show daemon selection. Confirm it's (a) a **full JDK**, not a JRE, and (b) within Gradle's **supported version range** for the wrapper-pinned Gradle version. Resolution paths, in order of preference: prefer a committed **Daemon JVM Criteria** so selection is portable; if someone set a bad `org.gradle.java.home`, correct or remove it (especially if it leaked into project `gradle.properties`); ensure the chosen JDK exists. After changing the selection, **stop stale daemons** (`./gradlew --stop`) because an existing daemon on the wrong JVM may be reused. For CI, pin the JVM explicitly and avoid depending on the agent's ambient `JAVA_HOME`.

code

bash · 9 lines
bash
# Which JVM is the daemon actually on?
./gradlew --version

# Migrate to a portable, committed selection
./gradlew updateDaemonJvm --jvm-version=17 --jvm-vendor=adoptium

# Kill stale daemons running on the wrong JVM, then rebuild
./gradlew --stop
./gradlew build

go deeper

for a junior

Know how to print the JVM in use (./gradlew --version) and that Gradle needs a JDK.

for a middle

Walk the precedence (criteria / org.gradle.java.home / JAVA_HOME) and validate version + JDK-vs-JRE.

for a senior

Systematically diagnose, prefer migrating to criteria, and clear stale daemons; reason about CI vs local mismatch.

for a principal

Establish guardrails: forbid committed absolute java.home, mandate criteria, and make CI fail fast on daemon-JVM drift.

## Diagnosing daemon-JVM selection failures ### Common symptoms - `Value '...' given for org.gradle.java.home Gradle property is invalid (Java home supplied is invalid)` — the path is wrong/missing. - `Gradle requires JVM X to Y. Your build is currently configured to use JVM Z.` — version out of supported range. - Daemon repeatedly starts and dies, or `Unable to start the daemon process`. - A JRE (no `javac`/no `tools`) was selected; Gradle needs a JDK. ### Step 1 — find the JVM actually in use ```bash ./gradlew --version # prints Gradle version AND the JVM running it ./gradlew buildEnvironment ./gradlew help --info # selection details in the log ``` This tells you whether the daemon is on the JDK you think it is. ### Step 2 — establish the precedence that produced it The daemon JVM is chosen by (roughly, highest to lowest): 1. **Daemon JVM Criteria** (`gradle/gradle-daemon-jvm.properties`) — if present, Gradle resolves a matching JDK. 2. **`org.gradle.java.home`** — from command-line `-D`, then project `gradle.properties`, then `~/.gradle/gradle.properties`. 3. **Launcher JVM / `JAVA_HOME`** — the JVM that started `gradlew`. Find which layer set the bad value. A frequent culprit: a stale absolute path committed into project `gradle.properties`. ### Step 3 — validate the candidate JVM - **Is it a JDK?** It must contain `bin/javac`; a JRE will fail. - **Is the version supported?** Each Gradle version supports a Java range. Running Gradle 8.x on a too-new or too-old JDK fails. Cross-check the wrapper-pinned Gradle version's compatibility matrix. - **Does the path exist?** For `org.gradle.java.home`, the directory must be present. ### Step 4 — fix the selection - Prefer migrating to a **committed criteria file** (`./gradlew updateDaemonJvm --jvm-version=17 --jvm-vendor=adoptium`) so the choice is portable and reproducible. - If a bad `org.gradle.java.home` is committed, remove it from project config; keep personal overrides only in `~/.gradle/gradle.properties`. - Ensure a suitable JDK is installed (or rely on provisioning where supported). ### Step 5 — clear stale daemons Gradle may reuse an already-running daemon on the wrong JVM. After changing selection: ```bash ./gradlew --stop ``` This kills idle daemons so the next build spins up one on the corrected JVM. ### CI guidance Don't rely on the runner's ambient `JAVA_HOME`. Either pin via the criteria file (portable, preferred) or set a known-good JDK in the pipeline. Treat 'works on my machine, fails in CI' as a daemon-JVM-selection mismatch until proven otherwise.

  • Why might fixing org.gradle.java.home not immediately fix the build?
    An existing daemon on the old JVM can be reused. Run ./gradlew --stop so a new daemon starts on the corrected JVM.
  • How do you tell whether the selected Java home is a JDK or a JRE?
    A JDK contains bin/javac and toolchain binaries; a JRE doesn't. Gradle requires a JDK and fails on a JRE-only path.
  • Where would you look first if the build works locally but the daemon fails on CI?
    The JVM selection: ambient JAVA_HOME on the runner versus the committed criteria. Pin the daemon JVM via criteria so CI matches local.

saying these in an interview costs you the question

  • Pointing org.gradle.java.home at a JRE and expecting it to work.
  • Forgetting that a stale daemon can be reused after you change the JVM.
  • Assuming any modern JDK works — each Gradle version has a supported Java range.

context