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
- Delete the partially downloaded Java runtime directory/cache and re-download.
- Check free disk space on the drive holding the temp directory.
- Exclude HMCL/temp dirs from antivirus or close programs locking the files, then retry.
- 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
- Re-download after any interrupted transfer instead of resuming partially.
- Keep several GB free on the drive holding HMCL's data directory.
- Exclude the game/HMCL directories from antivirus scanning.
- Use a stable connection or a closer mirror for large runtime downloads.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Malformed forge installer file
- Malformed neoforge installer file
- Asset index file malformed
- Failed to download texture
- Expecting file in terracotta bundle.
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)