HMCL-dev/HMCL · error · IOException

"Mod " + modFile + " `mcmod.info` is malformed"

Error message

"Mod " + modFile + " `mcmod.info` is malformed"

What it means

ForgeOldModMetadata.fromFile parses a legacy Forge mod's `mcmod.info` file. When the top-level JSON object deserializes to null (e.g. the file contains `null` or an unparseable-but-lexed object), an IOException with this message is thrown because no mod metadata could be extracted.

Solutions

  1. Open the mod jar and verify `mcmod.info` contains a valid object with a non-empty `modList` array
  2. Re-download or rebuild the mod from a trusted source
  3. Remove the offending jar from the mods folder if it is not a real Forge mod

Example fix

// before (malformed mcmod.info)
null
// after
{
  "modList": [{ "modid": "example", "name": "Example", "version": "1.0" }]
}
Defensive patterns

Strategy: try-catch

Validate before calling

try (var z = new ZipFile(jar.toFile())) {
    var e = z.getEntry("mcmod.info");
    if (e == null) throw new IllegalStateException("no mcmod.info");
    String s = new String(z.getInputStream(e).readAllBytes(), StandardCharsets.UTF_8);
    if (s.isBlank() || s.trim().equals("null")) throw new IllegalStateException("blank mcmod.info");
}

Try / catch

try {
    LocalModFile f = ForgeOldModMetadata.fromFile(modManager, modFile, tree);
} catch (IOException | JsonParseException e) {
    log.warn("Skipping mod with malformed mcmod.info: " + modFile, e);
}

Prevention

When it happens

Trigger: Calling ForgeOldModMetadata.fromFile on a jar whose `mcmod.info` first token is BEGIN_OBJECT but GSON's fromJson yields a null ForgeOldModMetadataLst (literal `null` content or empty/blank payload parsed leniently).

Common situations: Manually edited or truncated `mcmod.info` files inside old Forge jars; mods packaged by old build tools writing an empty metadata file; corrupted downloads.

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/3665b23050c3f82d. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java:143

    }

    public static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException {
        ZipArchiveEntry mcmod = tree.getEntry("mcmod.info");
        if (mcmod == null)
            throw new IOException("File " + modFile + " is not a Forge mod.");

        List<ForgeOldModMetadata> modList;

        try (var reader = tree.getBufferedReader(mcmod);
             var jsonReader = new JsonReader(reader)) {
            JsonToken firstToken = jsonReader.peek();

            if (firstToken == JsonToken.BEGIN_ARRAY)
                modList = JsonUtils.GSON.fromJson(jsonReader, listTypeOf(ForgeOldModMetadata.class));
            else if (firstToken == JsonToken.BEGIN_OBJECT) {
                ForgeOldModMetadataLst list = JsonUtils.GSON.fromJson(jsonReader, ForgeOldModMetadataLst.class);
                if (list == null)
                    throw new IOException("Mod " + modFile + " `mcmod.info` is malformed");
                modList = list.modList();
            } else {
                throw new JsonParseException("Unexpected first token: " + firstToken);
            }
        }

        if (modList == null || modList.isEmpty())
            throw new IOException("Mod " + modFile + " `mcmod.info` is malformed");
        ForgeOldModMetadata metadata = modList.get(0);
        String authors = metadata.getAuthor();
        if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0)
            authors = String.join(", ", metadata.getAuthors());
        if (StringUtils.isBlank(authors) && metadata.getAuthorList().length > 0)
            authors = String.join(", ", metadata.getAuthorList());
        if (StringUtils.isBlank(authors))
            authors = metadata.getCredits();
        return new LocalModFile(modManager, modManager.getLocalMod(metadata.getModId(), ModLoaderType.FORGE), modFile, metadata.getName(), new LocalAddonFile.Description(metadata.getDescription()),
                authors, metadata.getVersion(), metadata.getGameVersion(),

View on GitHub (pinned to 24702dc5a0)