HMCL-dev/HMCL · error · IOException
Not a valid world zip file since level.dat or…
Error message
Not a valid world zip file since level.dat or special_level.dat cannot be found.
What it means
Thrown by World(Path) when a zip-based world archive does not contain level.dat at its root (or a nested single root directory), nor the special_level.dat variant used by the 20w14infinite April Fools snapshot. HMCL requires one of these NBT files to parse world metadata, so an archive without them cannot be a Minecraft world.
Solutions
- Verify the zip actually contains level.dat (or special_level.dat) at its root or exactly one top-level folder that contains it
- Re-export the world from Minecraft or HMCL so the archive includes level.dat
- If importing a 20w14infinite world, confirm special_level.dat is present inside the zip
- Check the zip is not truncated or corrupted (test unzip) and retry
Example fix
// before
World world = new World(Path.of("download.zip")); // zip has no level.dat
// after
try (FileSystem fs = CompressingUtils.readonly(Path.of("download.zip")).build()) {
if (!Files.exists(fs.getPath("/level.dat"))) throw new IllegalArgumentException("zip lacks level.dat");
}
World world = new World(Path.of("download.zip")); Defensive patterns
Strategy: validation
Validate before calling
static boolean looksLikeWorldZip(Path zip) throws IOException {
try (FileSystem fs = CompressingUtils.readonly(zip).setAutoDetectEncoding(true).build()) {
if (Files.isRegularFile(fs.getPath("/level.dat")) || Files.isRegularFile(fs.getPath("/special_level.dat"))) return true;
try (Stream<Path> s = Files.list(fs.getPath("/"))) {
List<Path> top = s.toList();
return top.size() == 1 && Files.isDirectory(top.get(0))
&& (Files.isRegularFile(top.get(0).resolve("level.dat")) || Files.isRegularFile(top.get(0).resolve("special_level.dat")));
}
}
} Type guard
static boolean isWorldZip(Path p) {
return Files.isRegularFile(p) && p.toString().endsWith(".zip");
} Try / catch
try {
World world = new World(zipPath);
} catch (IOException e) {
if (String.valueOf(e.getMessage()).contains("Not a valid world zip")) {
// surface 'archive has no level.dat' to the user and abort import
}
} Prevention
- Export worlds with the game's 'Edit > Export World' or HMCL itself so level.dat is always included
- Inspect the zip layout (unzip -l) before importing third-party archives
- Remember 20w14infinite worlds use special_level.dat, not level.dat
- Reject empty or zero-byte zips before constructing World
When it happens
Trigger: Constructing new World(zipPath) where file.isDirectory() is false and the zip archive contains no root-level level.dat and no special_level.dat after resolving a single top-level subdirectory.
Common situations: Exporting a world incorrectly (zip created without level.dat), archiving only part of the saves folder, passing a random zip file or an empty/corrupted zip, or a zip with multiple nested directories where level.dat is deeper than one level.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- Path cannot be recognized as a Minecraft world
- Theme pack does not contain
- Malformed datapack zip
- Not a valid world zip file
- Not a valid world directory
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/f964676428fc4006.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java:107
fileName = FileUtils.getName(this.file);
} else {
try (Stream<Path> filesStream = Files.list(fs.getPath("/"))) {
List<Path> files = filesStream.toList();
if (files.size() != 1 || !Files.isDirectory(files.get(0))) {
throw new IOException("Not a valid world zip file");
}
root = files.get(0);
fileName = FileUtils.getName(root);
}
}
Path levelDat = root.resolve("level.dat");
if (!Files.exists(levelDat)) { //version 20w14infinite
levelDat = root.resolve("special_level.dat");
}
if (!Files.exists(levelDat)) {
throw new IOException("Not a valid world zip file since level.dat or special_level.dat cannot be found.");
}
loadAndCheckLevelData(levelDat);
Path iconFile = root.resolve("icon.png");
if (Files.isRegularFile(iconFile)) {
try (InputStream inputStream = Files.newInputStream(iconFile)) {
icon = new Image(inputStream, 64, 64, true, false);
if (icon.isError())
throw icon.getException();
} catch (Exception e) {
LOG.warning("Failed to load world icon", e);
}
}
}
else
throw new IOException("Path " + file + " cannot be recognized as a Minecraft world");
}
View on GitHub (pinned to 24702dc5a0)