HMCL-dev/HMCL · error · ArtifactMalformedException

Malformed forge installer file

Error message

Malformed forge installer file

What it means

During preExecute, ForgeNewInstallTask opens the downloaded Forge installer jar as a read-only zip filesystem. If the file is not a valid zip archive (java.util.zip.ZipException), it wraps the failure in ArtifactMalformedException("Malformed forge installer file"). The installer download is corrupt, incomplete, or not a jar at all (e.g. an HTML error page).

Solutions

  1. Delete the cached Forge installer file and re-download (clear HMCL download cache)
  2. Check disk space and network/proxy stability, then retry installation
  3. Verify the file is a real jar: 'file installer.jar' or try opening it with a zip tool
  4. Switch download source/mirror in HMCL settings and retry
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-validate the installer is a zip before invoking the task
try (var zin = new java.util.zip.ZipInputStream(Files.newInputStream(installer))) {
    if (zin.getNextEntry() == null) throw new IllegalStateException("not a zip/jar: " + installer);
}

Try / catch

try { installTask.execute(); } catch (ArtifactMalformedException e) { Files.deleteIfExists(installer); reDownloadForgeInstaller(); }

Prevention

When it happens

Trigger: installer Path passed to ForgeNewInstallTask points to a file that CompressingUtils.createReadOnlyZipFileSystem cannot open as zip — corrupt download, truncated file, HTML/blocked response saved as .jar, or wrong file passed as installer.

Common situations: Unstable network/mirror corrupting the download; download servers returning error pages; proxy/antivirus truncating files; user manually placing a wrong file in the cache; disk full during download.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — 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/94e756042f86af9a. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java:326

            forgeVersion = JsonUtils.fromNonNullJson(Files.readString(fs.getPath(profile.getJson())), GameInstanceManifest.class);

            for (Library library : profile.getLibraries()) {
                Path file = fs.getPath("maven").resolve(library.getPath());
                if (Files.exists(file)) {
                    Path dest = gameRepository.getLayout().getLibraryFile(manifest.id(), library);
                    FileUtils.copyFile(file, dest);
                }
            }

            if (profile.getPath().isPresent()) {
                Path mainJar = profile.getPath().get().getPath(fs.getPath("maven"));
                if (Files.exists(mainJar)) {
                    Path dest = gameRepository.getLayout().getArtifactFile(profile.getPath().get());
                    FileUtils.copyFile(mainJar, dest);
                }
            }
        } catch (ZipException ex) {
            throw new ArtifactMalformedException("Malformed forge installer file", ex);
        }

        dependents.add(new GameLibrariesTask(dependencyManager, manifest, true, profile.getLibraries()));
    }

    private Map<String, String> parseOptions(List<String> args, Map<String, String> vars) {
        Map<String, String> options = new LinkedHashMap<>();
        String optionName = null;
        for (String arg : args) {
            if (arg.startsWith("--")) {
                if (optionName != null) {
                    options.put(optionName, "");
                }
                optionName = arg.substring(2);
            } else {
                if (optionName == null) {
                    // ignore
                } else {

View on GitHub (pinned to 24702dc5a0)