{"record":{"id":"3665b23050c3f82d","repo":"HMCL-dev/HMCL","slug":"mod-modfile-mcmod-info-is-malformed","errorCode":null,"errorMessage":"\"Mod \" + modFile + \" `mcmod.info` is malformed\"","messagePattern":"\"Mod \" \\+ modFile \\+ \" `mcmod\\.info` is malformed\"","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java","lineNumber":143,"sourceCode":"    }\n\n    public static LocalModFile fromFile(ModManager modManager, Path modFile, ZipFileTree tree) throws IOException, JsonParseException {\n        ZipArchiveEntry mcmod = tree.getEntry(\"mcmod.info\");\n        if (mcmod == null)\n            throw new IOException(\"File \" + modFile + \" is not a Forge mod.\");\n\n        List<ForgeOldModMetadata> modList;\n\n        try (var reader = tree.getBufferedReader(mcmod);\n             var jsonReader = new JsonReader(reader)) {\n            JsonToken firstToken = jsonReader.peek();\n\n            if (firstToken == JsonToken.BEGIN_ARRAY)\n                modList = JsonUtils.GSON.fromJson(jsonReader, listTypeOf(ForgeOldModMetadata.class));\n            else if (firstToken == JsonToken.BEGIN_OBJECT) {\n                ForgeOldModMetadataLst list = JsonUtils.GSON.fromJson(jsonReader, ForgeOldModMetadataLst.class);\n                if (list == null)\n                    throw new IOException(\"Mod \" + modFile + \" `mcmod.info` is malformed\");\n                modList = list.modList();\n            } else {\n                throw new JsonParseException(\"Unexpected first token: \" + firstToken);\n            }\n        }\n\n        if (modList == null || modList.isEmpty())\n            throw new IOException(\"Mod \" + modFile + \" `mcmod.info` is malformed\");\n        ForgeOldModMetadata metadata = modList.get(0);\n        String authors = metadata.getAuthor();\n        if (StringUtils.isBlank(authors) && metadata.getAuthors().length > 0)\n            authors = String.join(\", \", metadata.getAuthors());\n        if (StringUtils.isBlank(authors) && metadata.getAuthorList().length > 0)\n            authors = String.join(\", \", metadata.getAuthorList());\n        if (StringUtils.isBlank(authors))\n            authors = metadata.getCredits();\n        return new LocalModFile(modManager, modManager.getLocalMod(metadata.getModId(), ModLoaderType.FORGE), modFile, metadata.getName(), new LocalAddonFile.Description(metadata.getDescription()),\n                authors, metadata.getVersion(), metadata.getGameVersion(),","sourceCodeStart":125,"sourceCodeEnd":161,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java#L125-L161","documentation":"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.","triggerScenarios":"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).","commonSituations":"Manually edited or truncated `mcmod.info` files inside old Forge jars; mods packaged by old build tools writing an empty metadata file; corrupted downloads.","solutions":["Open the mod jar and verify `mcmod.info` contains a valid object with a non-empty `modList` array","Re-download or rebuild the mod from a trusted source","Remove the offending jar from the mods folder if it is not a real Forge mod"],"exampleFix":"// before (malformed mcmod.info)\nnull\n// after\n{\n  \"modList\": [{ \"modid\": \"example\", \"name\": \"Example\", \"version\": \"1.0\" }]\n}","handlingStrategy":"try-catch","validationCode":"try (var z = new ZipFile(jar.toFile())) {\n    var e = z.getEntry(\"mcmod.info\");\n    if (e == null) throw new IllegalStateException(\"no mcmod.info\");\n    String s = new String(z.getInputStream(e).readAllBytes(), StandardCharsets.UTF_8);\n    if (s.isBlank() || s.trim().equals(\"null\")) throw new IllegalStateException(\"blank mcmod.info\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    LocalModFile f = ForgeOldModMetadata.fromFile(modManager, modFile, tree);\n} catch (IOException | JsonParseException e) {\n    log.warn(\"Skipping mod with malformed mcmod.info: \" + modFile, e);\n}","preventionTips":["Never hand-edit mcmod.info; let the build plugin generate it","Verify jar integrity after download","Wrap per-mod metadata parsing so one bad jar doesn't abort a batch scan"],"tags":["java","io","json","mod-metadata"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}