HMCL-dev/HMCL · error · IOException

Failed to get Java version from

Error message

Failed to get Java version from 

What it means

JavaInfoUtils.fromExecutable successfully ran the org.glavo.info.Main helper but its JSON output did not include a java.version property, so the parsed Result record's javaVersion field is null. This IOException distinguishes 'executable responded but reported no version' from a total probe failure. HMCL requires a version string to classify and use a Java installation, so the runtime is treated as unusable.

Solutions

  1. Run `<executable> -XshowSettings:properties -version` and confirm `java.version` is set in that runtime.
  2. Use a standard, complete JDK/JRE build (Temurin, Zulu, Oracle, etc.) instead of the stripped/exotic runtime.
  3. Run the helper manually (`<exe> -classpath <hmcl.jar> org.glavo.info.Main`) and inspect the emitted JSON for a missing java.version key.
  4. Update the runtime to a newer release of the same distribution if the property is suppressed.
  5. Catch this IOException and fall back to another discovered Java installation.

Example fix

// before
JavaInfo info = JavaInfoUtils.fromExecutable(customMinimalJrePath);
// after
try {
    JavaInfo info = JavaInfoUtils.fromExecutable(customMinimalJrePath);
} catch (IOException e) {
    // runtime did not report java.version; pick a known-good JDK instead
    JavaInfo info = JavaInfoUtils.fromExecutable(Path.of("/usr/lib/jvm/temurin-17/bin/java"));
}
Defensive patterns

Strategy: try-catch

Validate before calling

Process p = new ProcessBuilder(exe.toString(), "-XshowSettings:properties", "-version").redirectErrorStream(true).start();
String out = new String(p.getInputStream().readAllBytes());
if (!out.contains("java.version")) LOG.warning("runtime reports no java.version: " + exe);

Try / catch

try {
    return JavaInfoUtils.fromExecutable(exe);
} catch (IOException e) {
    if (e.getMessage().startsWith("Failed to get Java version")) {
        LOG.warning("Runtime lacks java.version, skipping: " + exe);
    }
    return null; // or fall back to another runtime
}

Prevention

When it happens

Trigger: Calling JavaInfoUtils.fromExecutable(Path) on a runtime whose helper output JSON lacks `java.version` (Gson leaves the record field null).

Common situations: Minimal/exotic JRE builds (e.g. custom, embedded, or very old runtimes) that omit or mask the java.version system property; a wrapper script that mangles the helper's JSON output; a JRE built with patched system properties.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/c1c1366759d3f11f. Report an issue: GitHub.

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/java/JavaInfoUtils.java:63

        Path thisPath = JarUtils.thisJarPath();
        if (thisPath == null) {
            throw new IOException("Failed to find current HMCL location");
        }

        try {
            Result result = JsonUtils.GSON.fromJson(SystemUtils.run(
                    executable.toString(),
                    "-classpath",
                    thisPath.toString(),
                    org.glavo.info.Main.class.getName()
            ), Result.class);

            if (result == null) {
                throw new IOException("Failed to get Java info from " + executable);
            }

            if (result.javaVersion == null) {
                throw new IOException("Failed to get Java version from " + executable);
            }

            Architecture architecture = Architecture.parseArchName(result.osArch);
            Platform platform = Platform.getPlatform(OperatingSystem.CURRENT_OS,
                    architecture != Architecture.UNKNOWN
                            ? architecture
                            : Architecture.SYSTEM_ARCH);

            return new JavaInfo(platform, result.javaVersion, result.javaVendor);
        } catch (IOException e) {
            throw e;
        } catch (Throwable e) {
            throw new IOException(e);
        }
    }

    @JsonSerializable
    private record Result(@SerializedName("os.name") String osName, @SerializedName("os.arch") String osArch,

View on GitHub (pinned to 24702dc5a0)