HMCL-dev/HMCL · error · ArtifactMalformedException

File is malformed

Error message

File ${name} is malformed

What it means

During execution, MojangJavaDownloadTask decompresses each LZMA-compressed runtime file from the downloaded archive while computing its SHA-1. If reading or writing the decompressed data throws IOException, the file is considered corrupt/incomplete and ArtifactMalformedException wraps the cause, aborting the Java runtime installation.

Solutions

  1. Delete the partially downloaded Java runtime directory/cache and re-download.
  2. Check free disk space on the drive holding the temp directory.
  3. Exclude HMCL/temp dirs from antivirus or close programs locking the files, then retry.
  4. Retry on a stable network or switch to another download provider/mirror.

Example fix

// Not caller-fixable; recovery:
// 1) Settings -> Java -> delete the broken managed Java runtime
// 2) re-trigger the Java download to get a fresh archive
Defensive patterns

Strategy: retry

Validate before calling

// Before install, verify archive size and free disk space
if (Files.size(archive) < expectedMinBytes) throw new IOException("Truncated download");
if (Files.getFileStore(tempDir).getUsableSpace() < requiredBytes) throw new IOException("Low disk space");

Type guard

null

Try / catch

try { task.run(); } catch (ArtifactMalformedException e) {
    LOG.warning("Runtime file corrupt: " + e.getMessage(), e);
    Files.deleteIfExists(runtimeDir); // then re-download once
}

Prevention

When it happens

Trigger: LZMAInputStream failing mid-decompression (truncated/corrupt download) or Files.copy failing on write, inside execute()'s per-entry dependency task for a runtime file entry.

Common situations: Interrupted or flaky network producing a truncated runtime archive; disk full or permission issues on the temp directory; antivirus locking the temporary file during decompression.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/download/java/mojang/MojangJavaDownloadTask.java:126

                    String rawSha1;
                    if (raw != null && raw.getSha1() != null) {
                        rawSha1 = raw.getSha1();
                    } else {
                        rawSha1 = null;
                    }

                    Path tempFile = tempDir.resolve(entry.getKey() + ".lzma");
                    var task = new FileDownloadTask(downloadProvider.injectURLWithCandidates(download.getUrl()), tempFile,
                            new FileDownloadTask.IntegrityCheck("SHA-1", download.getSha1()));
                    task.setName(entry.getKey());
                    dependencies.add(task.thenRunAsync(() -> {
                        Path decompressed = tempDir.resolve(entry.getKey() + ".tmp");
                        var digest = MessageDigest.getInstance("SHA-1");
                        try (var input = new DigestInputStream(new LZMAInputStream(Files.newInputStream(tempFile)), digest)) {
                            Files.copy(input, decompressed, StandardCopyOption.REPLACE_EXISTING);
                        } catch (IOException e) {
                            throw new ArtifactMalformedException("File " + entry.getKey() + " is malformed", e);
                        }

                        String actualSha1 = HexFormat.of().formatHex(digest.digest());

                        if (rawSha1 != null && !actualSha1.equalsIgnoreCase(rawSha1)) {
                            throw new ArtifactMalformedException("File " + entry.getKey() + " has incorrect SHA-1 hash: expected " + rawSha1 + ", got " + actualSha1);
                        }

                        try {
                            Files.deleteIfExists(tempFile);
                        } catch (IOException e) {
                            LOG.warning("Failed to delete temporary file: " + tempFile, e);
                        }

                        Files.move(decompressed, dest, StandardCopyOption.REPLACE_EXISTING);
                        if (file.isExecutable()) {
                            FileUtils.setExecutable(dest);
                        }

View on GitHub (pinned to 24702dc5a0)