HMCL-dev/HMCL · error · IOException
Asset index file malformed
Error message
Asset index file malformed
What it means
DefaultGameInstance.getAssetIndex() parses versions/<id>/assets/<assetId>.json (or the indexed asset index) into an AssetIndex. If the file is missing (NPE from Objects.requireNonNull after fromJsonFile returns null) or is not valid/parseable JSON (JsonParseException), it is wrapped in an IOException with message 'Asset index file malformed'. This means the Minecraft asset index metadata is absent or corrupted, not that assets themselves are missing.
Solutions
- Delete the corrupt assets/indexes/<assetId>.json and re-download it (relaunch the game or trigger the asset repair in HMCL)
- Verify the file exists before calling getAssetIndex and download it from Mojang's asset index URL if missing
- Catch this IOException and fall back to re-verifying/re-downloading game assets rather than failing the launch
- Check disk space and permissions if re-downloads keep producing truncated files
Example fix
// before
AssetIndex index = instance.getAssetIndex("17");
// after
Path indexFile = instance.getLayout().getAssetIndexFile("17");
if (!Files.isRegularFile(indexFile) || Files.size(indexFile) == 0) {
downloadAssetIndex("17");
}
AssetIndex index = instance.getAssetIndex("17"); Defensive patterns
Strategy: fallback
Validate before calling
Path f = instance.getLayout().getAssetIndexFile(assetId);
boolean ok = Files.isRegularFile(f) && Files.size(f) > 2; // 'null'/'{}' are tiny
if (!ok) downloadAssetIndex(assetId); Try / catch
try {
AssetIndex idx = instance.getAssetIndex(assetId);
} catch (IOException e) {
LOG.warning("Asset index corrupt/missing, re-downloading: " + e.getMessage());
downloadAssetIndex(assetId);
AssetIndex idx = instance.getAssetIndex(assetId);
} Prevention
- Check asset index files for 0-byte size after downloads complete
- Keep interrupted downloads atomic (temp file + move)
- Re-verify assets after crashes instead of trusting on-disk state
When it happens
Trigger: Calling getAssetIndex(assetId) when the asset index file does not exist on disk, was truncated by an interrupted download, or contains invalid JSON; getAssetObject() propagates this through assetObject().
Common situations: Interrupted game downloads leaving a zero-byte or partial asset index json; manual deletion during cleanup; disk corruption; pointing a custom instance at an assetId whose index was never downloaded.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Invalid theme-pack manifest
- "Mod " + modFile + " `mcmod.info` is malformed"
- File is malformed
- Unrecognized asset object
- json.toString()
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/cae17e4349fc41e9.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameInstance.java:302
return manifestFile.resolveSibling(FileUtils.getNameWithoutExtension(manifestFile) + ".jar");
}
return layout.getInstanceJarFile(id);
}
@Override
public Path getRunDirectory() {
// Official layout: shared working directory is the repository base directory.
return getRepository().getBaseDirectory();
}
/// {@inheritDoc}
@Override
public AssetIndex getAssetIndex(String assetId) throws IOException {
try {
return Objects.requireNonNull(
JsonUtils.fromJsonFile(getLayout().getAssetIndexFile(assetId), AssetIndex.class));
} catch (JsonParseException | NullPointerException e) {
throw new IOException("Asset index file malformed", e);
}
}
/// {@inheritDoc}
@Override
public Path getActualAssetDirectory(String assetId) {
try {
return reconstructAssets(assetId);
} catch (IOException | JsonParseException e) {
LOG.error("Unable to reconstruct asset directory", e);
return getLayout().getAssetDirectory();
}
}
/// {@inheritDoc}
@Override
public Optional<Path> getAssetObject(String assetId, String name) throws IOException {
try {View on GitHub (pinned to 24702dc5a0)