HMCL-dev/HMCL · error · JsonParseException
"Unexpected first token: " + firstToken
Error message
"Unexpected first token: " + firstToken
What it means
ForgeOldModMetadata.fromFile expects `mcmod.info` to start with either a JSON array (a mod list) or an object (`{"modList": ...}`). Any other leading token (string, number, boolean, END) triggers a JsonParseException naming the unexpected token.
Solutions
- Inspect `mcmod.info` in the jar and make the root a JSON array or an object with `modList`
- Re-obtain the mod jar from its official release
- Exclude the jar from scanning if it is not a Forge mod
Example fix
// before (invalid root)
"not a mod list"
// after
[{ "modid": "example", "name": "Example", "version": "1.0" }] Defensive patterns
Strategy: validation
Validate before calling
String s = readMcmodInfo(tree);
String t = s.trim();
if (!(t.startsWith("[") || t.startsWith("{")))
throw new IllegalStateException("mcmod.info must start with '[' or '{'"); Try / catch
try {
ForgeOldModMetadata.fromFile(modManager, modFile, tree);
} catch (JsonParseException e) {
log.warn("Unexpected mcmod.info structure: " + e.getMessage());
} Prevention
- Validate JSON root type with a quick peek at the first non-whitespace char
- Keep build-tool metadata generation enabled
- Sanity-check packaged resources in CI
When it happens
Trigger: Calling ForgeOldModMetadata.fromFile on a jar whose `mcmod.info` begins with a scalar like `"foo"`, `42`, or `true` instead of an array or object.
Common situations: Placeholder `mcmod.info` files written by hand; build scripts accidentally embedding a plain string; truncation leaving a partial scalar.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- "Mod " + modFile + " `mcmod.info` is malformed"
- "Mod " + modFile + " `litemod.json` is malformed."
- json.toString()
- Protected payload is not a
- Missing game manifest
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/c1c56f849f651219.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/addon/meta/ForgeOldModMetadata.java:146
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(),
StringUtils.isBlank(metadata.getUrl()) ? metadata.getUpdateUrl() : metadata.url,
metadata.getLogoFile());
}View on GitHub (pinned to 24702dc5a0)