HMCL-dev/HMCL · error · IOException

Missing release file

Error message

Missing release file 

What it means

JavaManagementPage.onAddJavaHome registers an external JDK directory with HMCL. A valid JDK/JRE installation must contain a `release` metadata file at its root; if file.resolve("release") does not exist, the page throws IOException("Missing release file " + releaseFile) and shows the i18n "java.add.failed" dialog.

Solutions

  1. Select the JDK root directory (the one containing both `bin/` and `release`), not bin/ or its parent
  2. Verify the `release` file exists: ls <chosen-dir>/release
  3. Re-extract the JDK archive if it was unpacked incompletely or files were stripped
  4. Use a full JDK distribution rather than a minimal/custom runtime image

Example fix

// before
Path releaseFile = file.resolve("release");
if (Files.notExists(releaseFile))
    throw new IOException("Missing release file " + releaseFile);
// after
// caller-side guard before invoking onAddJavaHome
if (file.getFileName().toString().equals("bin"))
    file = file.getParent(); // user picked <jdk>/bin, walk up to <jdk>
Defensive patterns

Strategy: validation

Validate before calling

// before calling onAddJavaHome
if (file.getFileName().toString().equals("bin"))
    file = file.getParent();
if (!Files.isRegularFile(file.resolve("release"))) {
    Controllers.dialog("Selected directory has no 'release' file — pick the JDK root folder");
    return;
}

Try / catch

try {
    addJavaHome(path);
} catch (IOException e) {
    if (e.getMessage().startsWith("Missing release file")) {
        Controllers.dialog("Pick the JDK root directory (contains bin/ and release), not bin/ or its parent");
    } else throw e;
}

Prevention

When it happens

Trigger: Using "Add Java home" and pointing at a directory that lacks a `release` file at its top level — e.g. the bin/ directory, a folder one level too high, or a stripped JRE image.

Common situations: Selecting <jdk>/bin instead of <jdk>; selecting the parent of the JDK; jlink/jpackage-produced minimal images that drop the release file; manually copied JDKs missing metadata files.

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/1970e34967275bc9. Report an issue: GitHub.

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/main/JavaManagementPage.java:149

            return;
        }
        Controllers.navigateForward(new JavaRestorePage(SettingsManager.userSettings().getDisabledJava()));
    }

    private void onAddJavaBinary(Path file) {
        JavaManager.getAddJavaTask(file).whenComplete(Schedulers.javafx(), exception -> {
            if (exception != null) {
                LOG.warning("Failed to add java", exception);
                Controllers.dialog(i18n("java.add.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
            }
        }).start();
    }

    private void onAddJavaHome(Path file) {
        Task.composeAsync(() -> {
            Path releaseFile = file.resolve("release");
            if (Files.notExists(releaseFile))
                throw new IOException("Missing release file " + releaseFile);
            return JavaManager.getAddJavaTask(file.resolve("bin").resolve(OperatingSystem.CURRENT_OS.getJavaExecutable()));
        }).whenComplete(Schedulers.javafx(), exception -> {
            if (exception != null) {
                LOG.warning("Failed to add java", exception);
                Controllers.dialog(i18n("java.add.failed"), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
            }
        }).start();
    }

    private void onInstallArchive(Path file) {
        Task.supplyAsync(() -> {
            try (ArchiveFileTree<?, ?> tree = ArchiveFileTree.open(file)) {
                JavaInfo info = JavaInfo.fromArchive(tree);

                if (!JavaManager.isCompatible(info.getPlatform()))
                    throw new UnsupportedPlatformException(info.getPlatform().toString());

                return Pair.pair(tree.getRoot().getSubDirs().keySet().iterator().next(), info);

View on GitHub (pinned to 24702dc5a0)