HMCL-dev/HMCL · error · IOException

"Invalid metadata file: " + jarInJarMetadata

Error message

"Invalid metadata file: " + jarInJarMetadata

What it means

Thrown by ForgeNewModMetadata.fromEmbeddedMod when META-INF/jarjar/metadata.json exists but deserializes to null, so HMCL cannot read the Forge jar-in-jar embedding manifest that lists the nested dependency jars.

Solutions

  1. Open META-INF/jarjar/metadata.json in the jar and check it is valid, non-empty JSON with a jars array.
  2. Re-download the mod jar — an empty/truncated metadata.json indicates corruption.
  3. Rebuild the mod with a current ForgeGradle/Loom version so jarjar metadata is emitted correctly.
  4. If the file is legitimately unreadable, remove the jar; HMCL cannot resolve its embedded dependencies.

Example fix

// before (metadata.json)
null
// after
{ "jars": [ { "identifier": { "group": "com.example", "artifact": "dep" }, "version": "1.0", "path": "META-INF/jarjar/dep-1.0.jar" } ] }
Defensive patterns

Strategy: validation

Validate before calling

ZipArchiveEntry jarjar = tree.getEntry("META-INF/jarjar/metadata.json");
if (jarjar != null) {
    String text = tree.readTextEntry(jarjar);
    if (text == null || text.isBlank() || text.trim().equals("null")) {
        throw new IllegalArgumentException("corrupt META-INF/jarjar/metadata.json");
    }
}

Try / catch

try {
    LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, modFile, tree, type);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Invalid metadata file")) {
        LOG.warning("Corrupt jarjar metadata.json in " + modFile);
    }
}

Prevention

When it happens

Trigger: fromFile -> fromEmbeddedMod: tree.getEntry("META-INF/jarjar/metadata.json") is non-null but JsonUtils.fromJsonFully(stream, JarInJarMetadata.class) returns null (e.g. the stream decodes to JSON null or the type cannot be populated).

Common situations: A mod jar with a truncated, empty, or 'null' jarjar/metadata.json; a metadata.json written by an incompatible Forge jarjar version; jar corruption from a bad download.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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

Appendix: source

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

            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");
            if (jarInJarMetadata != null) {
                JarInJarMetadata metadata = JsonUtils.fromJsonFully(tree.getInputStream(jarInJarMetadata), JarInJarMetadata.class);
                if (metadata == null)
                    throw new IOException("Invalid metadata file: " + jarInJarMetadata);

                metadata.validate();

                embeddedModFiles = new ArrayList<>();
                for (EmbeddedJarMetadata jar : metadata.jars) {
                    ZipArchiveEntry path = tree.getEntry(jar.path);
                    if (path != null) {
                        embeddedModFiles.add(path);
                    } else {
                        LOG.warning("Missing embedded-dependencies-mod: " + jar.path);
                    }
                }
            }
        }

        if (embeddedModFiles.isEmpty()) {
            throw new IOException("Missing embedded mods");
        }

View on GitHub (pinned to 24702dc5a0)