HMCL-dev/HMCL · error · IOException

"Missing MANIFEST.MF in file " + modFile

Error message

"Missing MANIFEST.MF in file " + modFile

What it means

Thrown by ForgeNewModMetadata.fromEmbeddedMod when the jar has no META-INF/MANIFEST.MF entry. The embedded-mod identification path builds metadata from the jar manifest attributes, so without a manifest it cannot proceed and gives up so fromFile can fall through to the toml path or fail outright.

Solutions

  1. Check that META-INF/MANIFEST.MF exists at the archive root of the jar.
  2. Re-download the mod — official builds always ship a manifest.
  3. If repackaging, use Gradle's jar task or preserve META-INF/MANIFEST.MF during repack.
  4. If the jar also lacks mods.toml, it is not a Forge 1.13+ mod; use the appropriate loader parser.

Example fix

// before
LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, strippedJar, tree, type); // fails: no MANIFEST.MF
// after
if (tree.getEntry("META-INF/MANIFEST.MF") == null) {
    throw new IllegalArgumentException(strippedJar + " lacks META-INF/MANIFEST.MF; re-download or repackage preserving the manifest");
}
LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, strippedJar, tree, type);
Defensive patterns

Strategy: validation

Validate before calling

if (tree.getEntry("META-INF/MANIFEST.MF") == null) {
    throw new IllegalArgumentException(modFile + " has no jar manifest; cannot parse embedded mod metadata");
}

Type guard

boolean hasManifest(ZipFileTree tree) { return tree.getEntry("META-INF/MANIFEST.MF") != null; }

Try / catch

try {
    LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, modFile, tree, type);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().contains("MANIFEST.MF")) {
        // repack preserving META-INF/MANIFEST.MF or skip the jar
    }
}

Prevention

When it happens

Trigger: fromFile -> fromEmbeddedMod on a jar where tree.getEntry("META-INF/MANIFEST.MF") returns null, and the embedded-mod identification cannot continue.

Common situations: A jar repackaged with a tool that dropped or relocated META-INF/MANIFEST.MF; jars built without a jar manifest; non-mod jars placed in the mods directory.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeNewModMetadata.java:256

            } catch (IOException e) {
                LOG.warning("Failed to parse MANIFEST.MF in file " + modFile);
            }
        }

        ModLoaderType type = analyzeLoader(tomlParseResult, mod.getModId(), modLoaderType);

        String logoPath = StringUtils.isNotBlank(mod.getLogoFile()) ? mod.getLogoFile() : metadata.getLogoFile();

        return new LocalModFile(modManager, modManager.getLocalMod(mod.getModId(), type), modFile, mod.getDisplayName(), new LocalAddonFile.Description(mod.getDescription()),
                mod.getAuthors(), jarVersion == null ? mod.getVersion() : mod.getVersion().replace("${file.jarVersion}", jarVersion), "",
                mod.getDisplayURL(),
                logoPath);
    }

    private static LocalModFile fromEmbeddedMod(ModManager modManager, Path modFile, ZipFileTree tree, ModLoaderType modLoaderType) throws IOException {
        ZipArchiveEntry manifestFile = tree.getEntry("META-INF/MANIFEST.MF");
        if (manifestFile == null)
            throw new IOException("Missing MANIFEST.MF in file " + modFile);

        Manifest manifest;
        try (InputStream input = tree.getInputStream(manifestFile)) {
            manifest = new Manifest(input);
        }

        List<ZipArchiveEntry> embeddedModFiles = List.of();

        String embeddedDependenciesMod = manifest.getMainAttributes().getValue("Embedded-Dependencies-Mod");
        if (embeddedDependenciesMod != null) {
            ZipArchiveEntry embeddedModFile = tree.getEntry(embeddedDependenciesMod);
            if (embeddedModFile == null) {
                LOG.warning("Missing embedded-dependencies-mod: " + embeddedDependenciesMod);
                throw new IOException();
            }
            embeddedModFiles = List.of(embeddedModFile);
        } else {
            ZipArchiveEntry jarInJarMetadata = tree.getEntry("META-INF/jarjar/metadata.json");

View on GitHub (pinned to 24702dc5a0)