HMCL-dev/HMCL · error · IOException

Malformed datapack zip

Error message

Malformed datapack zip

What it means

DataPack.installPack inspects a datapack archive to determine its layout: a 'datapacks' subdirectory means the zip contains multiple packs, a pack.mcmeta at the root means a single pack. If neither exists, the archive does not match any known datapack structure and this IOException is thrown.

Solutions

  1. Verify the zip contains pack.mcmeta at its root (or a datapacks/ folder) before importing
  2. Re-download the datapack from its source — the archive may be corrupted
  3. Unwrap nested folders: if the zip contains a folder/pack.mcmeta, rezip so pack.mcmeta is at the root
  4. Check you selected a datapack, not a resource pack or mod file

Example fix

// before: mypack.zip -> mypack/pack.mcmeta (nested)
// after: rezip so structure is mypack.zip -> pack.mcmeta
zip -r mypack.zip pack.mcmeta assets data
Defensive patterns

Strategy: validation

Validate before calling

try (java.util.zip.ZipFile zip = new java.util.zip.ZipFile(packFile.toFile())) {
    boolean hasMcmeta = zip.getEntry("pack.mcmeta") != null;
    boolean hasDatapacks = zip.getEntry("datapacks/") != null;
    if (!hasMcmeta && !hasDatapacks) {
        throw new IllegalArgumentException(packFile + " is not a valid datapack zip");
    }
}

Try / catch

try {
    dataPack.installPack(target);
} catch (IOException e) {
    if (e.getMessage().contains("Malformed datapack")) {
        LOG.warning("Selected file is not a valid datapack (missing pack.mcmeta / datapacks dir)");
        // prompt user to pick a correct file
    }
}

Prevention

When it happens

Trigger: Calling installPack on a zip file whose root contains neither a datapacks/ directory nor a pack.mcmeta — i.e. the file is not a valid Minecraft datapack.

Common situations: User selects the wrong file (a resource pack, mod, or random zip) in the datapack importer; a corrupted/partially downloaded datapack zip; a pack nested one level too deep so mcmeta sits in a subfolder.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10). Data as JSON: /api/errors/7692d1e8b33f0e18. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/addon/datapack/DataPack.java:74

    }

    public ObservableList<Pack> getPacks() {
        return packs;
    }

    public static void installPack(Path sourceDataPackPath, Path targetDataPackDirectory, GameVersionNumber gameVersionNumber) throws IOException {
        boolean containsMultiplePacks;
        Set<String> packs = new HashSet<>();
        try (FileSystem fs = CompressingUtils.readonly(sourceDataPackPath).setAutoDetectEncoding(true).build()) {
            Path dataPacks = fs.getPath("datapacks");
            Path mcmeta = fs.getPath("pack.mcmeta");

            if (Files.exists(dataPacks)) {
                containsMultiplePacks = true;
            } else if (Files.exists(mcmeta)) {
                containsMultiplePacks = false;
            } else {
                throw new IOException("Malformed datapack zip");
            }

            if (containsMultiplePacks) {
                try (Stream<Path> s = Files.list(dataPacks)) {
                    packs = s.map(FileUtils::getNameWithoutExtension).collect(Collectors.toSet());
                }
            } else {
                packs.add(FileUtils.getNameWithoutExtension(sourceDataPackPath));
            }

            try (DirectoryStream<Path> stream = Files.newDirectoryStream(targetDataPackDirectory)) {
                for (Path dir : stream) {
                    String packName = FileUtils.getName(dir);
                    if (FileUtils.getExtension(dir).equals(DISABLED_EXT)) {
                        packName = StringUtils.removeSuffix(packName, "." + DISABLED_EXT);
                    }
                    packName = FileUtils.getNameWithoutExtension(packName);
                    if (packs.contains(packName)) {

View on GitHub (pinned to 24702dc5a0)