HMCL-dev/HMCL · error · IOException

Missing release file

Error message

Missing release file

What it means

JavaInfo.fromArchive requires the JDK root directory inside the archive to contain a 'release' file — the standard marker file JDK distributions ship with version info (JAVA_VERSION, vendor, etc.). It throws IOException("Missing release file") when that entry is absent, because version metadata cannot be parsed without it.

Solutions

  1. Use a complete JDK distribution archive that includes the 'release' file at the JDK root.
  2. If building a custom runtime with jlink, generate a release file or obtain JavaInfo another way.
  3. Re-download the distribution from the vendor; a partial download may have dropped entries.
  4. Catch the IOException and fall back to running 'java -version' on the extracted binary to detect the version.
Defensive patterns

Strategy: validation

Validate before calling

E releaseEntry = tree.getRoot().getSubDirs().values().iterator().next().getFiles().get("release");
if (releaseEntry == null) {
    throw new IOException("Archive JDK root has no 'release' file; not a full JDK distribution");
}

Try / catch

try {
    JavaInfo info = JavaInfo.fromArchive(tree);
} catch (IOException e) {
    if (e.getMessage().contains("Missing release file")) {
        // fall back to running java -version on the extracted binary
    }
}

Prevention

When it happens

Trigger: JavaInfo.fromArchive(tree) succeeds the single-root-directory check, but jdkRoot.getFiles().get("release") returns null — the JDK directory inside the archive has no 'release' file at its root.

Common situations: Using a JRE-only or custom-built distribution that omits the release file; manually assembled JDK directories; some minimal/minimal-jlink images; archives stripped of 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/4bbab79200edcaa9. Report an issue: GitHub.

Appendix: source

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

            throw new IOException("Unknown operating system: " + osName);

        if (arch == Architecture.UNKNOWN)
            throw new IOException("Unknown architecture: " + osArch);

        if (javaVersion == null)
            throw new IOException("Missing Java version");

        return new JavaInfo(Platform.getPlatform(os, arch), javaVersion, vendor);
    }

    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) {

View on GitHub (pinned to 24702dc5a0)