HMCL-dev/HMCL · error · ArtifactMalformedException
Malformed forge installer file
Error message
Malformed forge installer file
What it means
Thrown when the NeoForge/Forge installer file cannot be read as a valid ZIP archive during preExecute. HMCL copies the installer's main JAR out of the downloaded archive via a zip filesystem, and a ZipException bubbling up means the installer download is corrupt or not a real zip.
Solutions
- Delete the corrupted installer/cache and re-download the installer (verify file size/checksum if available).
- Switch or retry the download provider/mirror candidates.
- Check free disk space and that the download URL is not behind an error page (open the URL in a browser).
Example fix
// before
catch (ZipException ex) {
throw new ArtifactMalformedException("Malformed forge installer file", ex);
}
// after
// verify the downloaded installer before installing
if (!CompressingUtils.checkZip(installerPath)) {
Files.deleteIfExists(installerPath); // force a fresh download
throw new ArtifactMalformedException("Malformed forge installer file", null);
} Defensive patterns
Strategy: validation
Validate before calling
if (!Files.exists(installerPath) || Files.size(installerPath) < 1024 || !CompressingUtils.checkZip(installerPath)) {
Files.deleteIfExists(installerPath); // force re-download
throw new IOException("Installer is not a valid zip: " + installerPath);
} Try / catch
try {
await(task);
} catch (ArtifactMalformedException e) {
// delete cached installer and retry once
Files.deleteIfExists(installerPath);
await(task);
} Prevention
- Validate the installer is a readable zip before installing
- Prefer official download candidates/mirrors with checksum verification
- Keep enough free disk space for downloads
When it happens
Trigger: Downloading a Forge/NeoForge installer whose bytes are a truncated transfer, an HTML error page saved as a .jar, or a corrupted cache entry; then FileUtils.copyFile(mainJar, dest) inside the opened zip filesystem throws ZipException.
Common situations: Unstable mirror/BMCLAPI candidates returning partial files, disk full during download, proxy intercepting the download, or reusing a stale cached installer after an interrupted 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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Malformed neoforge installer file
- Malformed forge installer file
- Malformed forge installer file
- Cannot fix optifine
- File is malformed
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/40a1d8d8b94a88c3.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/download/neoforge/NeoForgeOldInstallTask.java:310
neoForgeVersion = 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)