HMCL-dev/HMCL · error · IOException
"File " + modFile + " is not a Forge mod."
Error message
"File " + modFile + " is not a Forge mod."
What it means
Thrown by ForgeOldModMetadata.fromFile when the jar contains no mcmod.info entry. HMCL identifies legacy Forge (1.12 and older) mods by parsing mcmod.info at the jar root; without it the jar cannot be recognized as an old Forge mod.
Solutions
- Verify the jar contains mcmod.info at its root (unzip -l mod.jar | grep mcmod.info).
- For Forge 1.13+/NeoForge jars, use ForgeNewModMetadata.fromFile/fromNeoForgeFile instead.
- Re-download the mod; official legacy builds always include mcmod.info.
- If repackaging a legacy mod, keep mcmod.info at the archive root.
Example fix
// before
LocalModFile mod = ForgeOldModMetadata.fromFile(modManager, modernForgeJar, tree); // throws: no mcmod.info
// after
if (tree.getEntry("mcmod.info") == null && tree.getEntry("META-INF/mods.toml") != null) {
LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, modernForgeJar, tree, ModLoaderType.FORGE);
} Defensive patterns
Strategy: validation
Validate before calling
if (tree.getEntry("mcmod.info") == null) {
// legacy Forge parser will throw; route elsewhere
return null;
} Type guard
boolean isLegacyForgeMod(ZipFileTree tree) { return tree.getEntry("mcmod.info") != null; } Try / catch
try {
LocalModFile mod = ForgeOldModMetadata.fromFile(modManager, modFile, tree);
} catch (IOException e) {
LOG.warning("Skipping jar without mcmod.info: " + modFile);
} Prevention
- Detect loader by entry presence: mcmod.info (legacy Forge), mods.toml (1.13+ Forge), fabric.mod.json (Fabric)
- Preserve mcmod.info at the archive root when repackaging legacy mods
- Re-download legacy mods from official sources rather than hand-repackaging
When it happens
Trigger: Calling ForgeOldModMetadata.fromFile(modManager, modFile, tree) on a jar whose ZipFileTree has no entry named 'mcmod.info'.
Common situations: Pointing the legacy parser at a 1.13+ Forge/NeoForge mod (uses mods.toml) or a Fabric mod (fabric.mod.json); a repackaged jar where mcmod.info was deleted; non-mod library jars in the mods folder.
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
- File is not a Fabric mod.
- "File " + modFile + " is not a Forge 1.13+ or NeoForge mod."
- Theme pack directory does not contain
- Theme pack does not contain
- Missing release file
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/0d568dbbb73c7518.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java:130
return updateUrl;
}
public String getCredits() {
return credits;
}
public String[] getAuthorList() {
return authorList;
}
public String[] getAuthors() {
return authors;
}
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);
}
}View on GitHub (pinned to 24702dc5a0)