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

  1. Delete the corrupt assets/indexes/<assetId>.json and re-download it (relaunch the game or trigger the asset repair in HMCL)
  2. Verify the file exists before calling getAssetIndex and download it from Mojang's asset index URL if missing
  3. Catch this IOException and fall back to re-verifying/re-downloading game assets rather than failing the launch
  4. 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

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.

Related errors


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)