HMCL-dev/HMCL · error · IOException

Missing java executable file

Error message

Missing java executable file

What it means

After parsing the release file, JavaInfo.fromArchive verifies the JDK directory contains a bin/ subdirectory with the platform's java executable (java, javaw.exe, etc.). It throws IOException("Missing java executable file") when either bin/ is absent or the expected executable entry is missing, since the archive does not contain a runnable Java runtime.

Solutions

  1. Verify the archive matches the target platform so the expected executable name (java vs javaw.exe) exists in bin/.
  2. Use a full JDK/JRE distribution instead of a stripped or jlink-minimized image lacking bin/java.
  3. For macOS, ensure the archive is laid out so bin/ and release are at the JDK root HMCL expects.
  4. Re-download the archive; a truncated download may have dropped bin entries.
Defensive patterns

Strategy: validation

Validate before calling

ArchiveFileTree.Dir<?> binDir = tree.getRoot().getSubDirs().get("bin");
String exe = expectedPlatform.getOperatingSystem().getJavaExecutable();
if (binDir == null || binDir.getFiles().get(exe) == null) {
    throw new IOException("Archive JDK has no bin/" + exe);
}

Try / catch

try {
    JavaInfo info = JavaInfo.fromArchive(tree);
} catch (IOException e) {
    if (e.getMessage().contains("Missing java executable")) {
        // verify archive platform matches target OS
    }
}

Prevention

When it happens

Trigger: JavaInfo.fromArchive(tree) with an archive whose single root dir lacks a 'bin' subdirectory, or whose bin/ does not contain the executable named by info.getPlatform().getOperatingSystem().getJavaExecutable() (e.g. using a linux archive on Windows lookup, or a stripped image).

Common situations: Downloading a JDK for the wrong platform (no javaw.exe on Windows archives is fine, but a mismatched archive lookup fails); jlink custom runtimes missing bin/java; archives with executables under a different layout (e.g. Contents/Home on macOS).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/java/JavaInfo.java:104

    }

    public static <F, E extends ArchiveEntry> JavaInfo fromArchive(ArchiveFileTree<F, E> tree) throws IOException {
        if (tree.getRoot().getSubDirs().size() != 1 || !tree.getRoot().getFiles().isEmpty())
            throw new IOException();

        ArchiveFileTree.Dir<E> jdkRoot = tree.getRoot().getSubDirs().values().iterator().next();
        E releaseEntry = jdkRoot.getFiles().get("release");
        if (releaseEntry == null)
            throw new IOException("Missing release file");

        JavaInfo info;
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(tree.getInputStream(releaseEntry), StandardCharsets.UTF_8))) {
            info = JavaInfo.fromReleaseFile(reader);
        }

        ArchiveFileTree.Dir<E> binDir = jdkRoot.getSubDirs().get("bin");
        if (binDir == null || binDir.getFiles().get(info.getPlatform().getOperatingSystem().getJavaExecutable()) == null)
            throw new IOException("Missing java executable file");

        return info;
    }

    public static String normalizeVendor(String vendor) {
        if (vendor == null)
            return null;

        return switch (vendor) {
            case "N/A" -> null;
            case "Oracle Corporation" -> "Oracle";
            case "Azul Systems, Inc." -> "Azul";
            case "IBM Corporation", "International Business Machines Corporation", "Eclipse OpenJ9" -> "IBM";
            case "Eclipse Adoptium" -> "Adoptium";
            case "Amazon.com Inc." -> "Amazon";
            default -> vendor;
        };
    }

View on GitHub (pinned to 24702dc5a0)