apache/cordova-android · error · CordovaError

Failed to run "javac -version", make sure that you have a JD

Error message

Failed to run "javac -version", make sure that you have a JDK version 8 installed.
You can get it from the following location:
https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

What it means

java.getVersion runs `javac -version` via execa; if the process fails to spawn (typically ENOENT - no javac on PATH, i.e. no JDK or only a JRE) it emits the execa shortMessage on verbose and throws this CordovaError. When JAVA_HOME is set but its bin/javac is unusable, the message additionally appends 'Your JAVA_HOME is invalid: <value>'. Note the text recommends JDK 8 - this code targets an older cordova-android; newer releases require JDK 17.

Source

Thrown at lib/env/java.js:58

    getVersion: async () => {
        await java._ensure(process.env);

        // Java <= 8 writes version info to stderr, Java >= 9 to stdout
        let javacOutput;
        try {
            javacOutput = (await execa('javac', ['-version'], { all: true })).all;
        } catch (ex) {
            events.emit('verbose', ex.shortMessage);

            let msg =
            'Failed to run "javac -version", make sure that you have a JDK version 8 installed.\n' +
            'You can get it from the following location:\n' +
            'https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html';
            if (process.env.JAVA_HOME) {
                msg += '\n\n';
                msg += 'Your JAVA_HOME is invalid: ' + process.env.JAVA_HOME;
            }
            throw new CordovaError(msg);
        }

        // We have to filter the output, because javac prints _JAVA_OPTIONS
        // before printing the version which causes semver.coerce to fail to get
        // the correct version if those options contain any numbers.
        const match = /javac\s+([\d.]+)/i.exec(javacOutput);
        return semver.coerce(match && match[1]);
    },

    /**
     * Ensures that Java is installed. Will throw exception if not.
     * Will set JAVA_HOME and PATH environment variables.
     *
     * This function becomes a no-op if already ran previously.
     */
    _ensure: async (environment) => {
        if (javaIsEnsured) {
            return;

View on GitHub (pinned to 7c1e190064)

Solutions

  1. Install a JDK matching your cordova-android version (8 for this codebase; cordova-android >= 13 needs JDK 17).
  2. Set JAVA_HOME to the JDK root such that $JAVA_HOME/bin/javac exists, and put $JAVA_HOME/bin on PATH.
  3. Open a new shell (or `hash -r`) after PATH changes so the old 'command not found' cache clears.

Example fix

# before
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-jre    # JRE: no javac

# after
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64  # JDK: bin/javac exists
export PATH="$JAVA_HOME/bin:$PATH"
Defensive patterns

Strategy: validation

Validate before calling

const javac = await execa('javac', ['-version'], { all: true }).catch(() => null);
if (!javac) {
  throw new Error('No usable javac on PATH - install a JDK and set JAVA_HOME so $JAVA_HOME/bin/javac exists');
}

Try / catch

try {
  await check_reqs.check_java();
} catch (e) {
  if (/Failed to run "javac -version"/.test(e.message)) {
    // JDK missing/invalid: install JDK, fix JAVA_HOME, open a new shell, rerun `cordova requirements android`
  }
  throw e;
}

Prevention

When it happens

Trigger: check_java / `cordova requirements android` / any build when `javac` is not resolvable: JDK not installed, only a JRE installed (java exists but javac does not), or JAVA_HOME pointing at a JRE/deleted directory.

Common situations: CI images shipping a JRE only; JAVA_HOME left stale after a JDK upgrade or OS cleanup; fresh machine with no Java; PATH ordering hiding the right JDK.

Related errors


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