HMCL-dev/HMCL · error · IOException
"File " + modFile + " is not a Forge 1.13+ or NeoForge mod."
Error message
"File " + modFile + " is not a Forge 1.13+ or NeoForge mod."
What it means
Thrown by ForgeNewModMetadata.fromFile as the last-resort failure after fromEmbeddedMod (which handles Forge 1.13+/NeoForge jars with META-INF/MANIFEST.MF or jar-in-jar data) also raised an exception. It means the jar neither exposes the expected mods.toml/neoforge.mods.toml nor embeds the manifest data HMCL needs, so it cannot be recognized as a Forge 1.13+/NeoForge mod.
Solutions
- Check the jar for META-INF/mods.toml or META-INF/neoforge.mods.toml (unzip -l mod.jar); if absent, it is not a 1.13+ Forge/NeoForge mod.
- For 1.12.2-and-older mods, use ForgeOldModMetadata.fromFile instead.
- Re-download the mod; a truncated or corrupt jar fails both parse paths.
- Confirm the mod is actually a Forge/NeoForge build, not a Fabric build, before calling this parser.
Example fix
// before
LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, modFile, tree, ModLoaderType.FORGE); // throws on 1.12 mods
// after
if (tree.getEntry("META-INF/mods.toml") == null && tree.getEntry("META-INF/MANIFEST.MF") == null) {
throw new IllegalArgumentException(modFile + " is not a Forge 1.13+ mod; try ForgeOldModMetadata");
}
LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, modFile, tree, ModLoaderType.FORGE); Defensive patterns
Strategy: try-catch
Validate before calling
boolean looksLikeModernForge(ZipFileTree tree) {
return tree.getEntry("META-INF/mods.toml") != null
|| tree.getEntry("META-INF/neoforge.mods.toml") != null
|| tree.getEntry("META-INF/MANIFEST.MF") != null;
} Try / catch
try {
LocalModFile mod = ForgeNewModMetadata.fromFile(modManager, modFile, tree, loaderType);
} catch (IOException | JsonParseException e) {
// fall back to legacy parser or skip the jar
} Prevention
- Detect mod loader before choosing the parser
- Use ForgeOldModMetadata for 1.12-and-older jars
- Treat failures of both paths as 'not a modern Forge mod' rather than retrying
When it happens
Trigger: Calling ForgeNewModMetadata.fromFile (directly or via fromForgeFile/fromNeoForgeFile/fromEmbeddedMod) on a jar where the toml entry is absent AND the embedded-mod path failed (e.g. no MANIFEST.MF, no jarjar metadata, or embedded entries unreadable).
Common situations: Scanning a legacy Forge 1.12 mod (uses mcmod.info) through the modern Forge parser; a broken or truncated jar; a Fabric or plain library jar misidentified as Forge.
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.
- "Mod " + modFile + " ` ` is…
- "Missing MANIFEST.MF in file " + modFile
- "Invalid metadata file: " + jarInJarMetadata
- Missing embedded mods
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/7729f68c4fcdc556.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeNewModMetadata.java:210
if (modLoaderType == ModLoaderType.NEO_FORGE) {
try {
return fromFile0("META-INF/neoforge.mods.toml", modLoaderType, modManager, modFile, tree);
} catch (Exception ignored) {
}
}
try {
return fromFile0("META-INF/mods.toml", modLoaderType, modManager, modFile, tree);
} catch (Exception ignored) {
}
try {
return fromEmbeddedMod(modManager, modFile, tree, modLoaderType);
} catch (Exception ignored) {
}
throw new IOException("File " + modFile + " is not a Forge 1.13+ or NeoForge mod.");
}
private static LocalModFile fromFile0(
String tomlPath,
ModLoaderType modLoaderType,
ModManager modManager,
Path modFile,
ZipFileTree tree) throws IOException, JsonParseException {
ZipArchiveEntry modToml = tree.getEntry(tomlPath);
if (modToml == null)
throw new IOException("File " + modFile + " is not a Forge 1.13+ or NeoForge mod.");
TomlParseResult tomlParseResult = Toml.parse(tree.readTextEntry(modToml));
if (tomlParseResult.hasErrors()) {
var ioException = new IOException("Mod " + modFile + " `%s` is malformed..".formatted(modToml.getName()));
tomlParseResult.errors().forEach(ioException::addSuppressed);
throw ioException;
}
ForgeNewModMetadata metadata = JsonUtils.GSON.fromJson(tomlParseResult.toJson(), ForgeNewModMetadata.class);View on GitHub (pinned to 24702dc5a0)