apache/cordova-android · error · CordovaError

Failed to find 'JAVA_HOME' environment variable. Try setting

Error message

Failed to find 'JAVA_HOME' environment variable. Try setting it manually.

What it means

Branch of java env detection: JAVA_HOME unset, javac found on PATH, and on macOS the helper /usr/libexec/java_home exists. The code execs it to discover JAVA_HOME; if that command exits non-zero (no JVM registered with macOS) it throws the generic 'Failed to find JAVA_HOME' message.

Source

Thrown at lib/env/java.js:97

        const javaHome = environment.CORDOVA_JAVA_HOME || environment.JAVA_HOME;
        if (javaHome) {
            // Ensure that CORDOVA_JAVA_HOME overrides
            environment.JAVA_HOME = javaHome;
            // Ensure that the JAVA_HOME bin path is before anything else
            // to cover cases where different Java versions is in the PATH
            environment.PATH = path.join(environment.JAVA_HOME, 'bin') + path.delimiter + environment.PATH;
        } else {
            const javacPath = utils.forgivingWhichSync('javac');
            if (javacPath) {
                // OS X has a command for finding JAVA_HOME.
                const find_java = '/usr/libexec/java_home';
                const default_java_error_msg = 'Failed to find \'JAVA_HOME\' environment variable. Try setting it manually.';
                if (fs.existsSync(find_java)) {
                    try {
                        environment.JAVA_HOME = (await execa(find_java)).stdout;
                    } catch (ex) {
                        events.emit('verbose', ex.shortMessage);
                        throw new CordovaError(default_java_error_msg);
                    }
                } else {
                    // See if we can derive it from javac's location.
                    const maybeJavaHome = path.dirname(path.dirname(javacPath));
                    if (fs.existsSync(path.join(maybeJavaHome, 'bin', 'java')) ||
                            fs.existsSync(path.join(maybeJavaHome, 'bin', 'java.exe'))) {
                        environment.JAVA_HOME = maybeJavaHome;
                    } else {
                        throw new CordovaError(default_java_error_msg);
                    }
                }
            } else if (utils.isWindows()) {
                const baseDirs = [environment.ProgramFiles, environment['ProgramFiles(x86)']];
                const globOpts = { absolute: true, onlyDirectories: true };
                const flatMap = (arr, f) => [].concat(...arr.map(f));
                const jdkDir = flatMap(baseDirs, cwd => {
                    return glob.sync('java/jdk*', { cwd, ...globOpts });
                }

View on GitHub (pinned to 7c1e190064)

Solutions

  1. Install a proper JDK on macOS (e.g. `brew install --cask temurin@8` or the Oracle installer) so /usr/libexec/java_home can find it.
  2. Or bypass detection: export JAVA_HOME manually to the JDK root.
  3. Diagnose registrations with `/usr/libexec/java_home -V`.

Example fix

# before
$ /usr/libexec/java_home
Unable to find any JVM in this Home.

# after
$ brew install --cask temurin@8
$ /usr/libexec/java_home   # prints the JDK root
Defensive patterns

Strategy: validation

Validate before calling

if (!process.env.JAVA_HOME && process.platform === 'darwin' && fs.existsSync('/usr/libexec/java_home')) {
  const probe = await execa('/usr/libexec/java_home').catch(() => null);
  if (!probe || !probe.stdout) {
    throw new Error('No JVM registered with macOS - install a JDK (brew install --cask temurin@8) or export JAVA_HOME');
  }
}

Try / catch

try {
  await check_reqs.check_java();
} catch (e) {
  if (/Failed to find 'JAVA_HOME'/.test(e.message) && process.platform === 'darwin') {
    // run `/usr/libexec/java_home -V` and either register a JDK or export JAVA_HOME manually
  }
  throw e;
}

Prevention

When it happens

Trigger: `cordova requirements android` / build on macOS where javac is reachable (shim, manual install) but /usr/libexec/java_home reports 'Unable to find any JVM in this Home' - i.e. no JDK registered via the standard macOS mechanism.

Common situations: JDK removed by deleting files instead of uninstalling; a JDK unpacked manually to /opt without being registered; Homebrew-installed javac without the Cask JDK; macOS updates clearing old JVM registrations.

Related errors


AI-assisted analysis of apache/cordova-android@7c1e190064 (2026-08-22). Data as JSON: /api/errors/80ceb53c9a35fc9a. Report an issue: GitHub.