HMCL-dev/HMCL · error · IOException
Not a valid world zip file
Error message
Not a valid world zip file
What it means
When a world file is a zip, the constructor expects either level.dat/special_level.dat at the zip root or exactly one top-level directory containing them. Otherwise it cannot locate the world root and throws IOException 'Not a valid world zip file'.
Solutions
- Re-zip so the archive contains exactly one top-level directory holding level.dat
- Extract the zip and import via the directory path instead
- Remove stray root-level files (e.g. __MACOSX, README) and keep a single world folder
- Download the world zip again from the original source
Example fix
// before
world.zip
├── level.dat
├── region/
└── screenshot.png // extra root entries -> rejected
// after
world.zip
└── MyWorld/
├── level.dat
└── region/ Defensive patterns
Strategy: validation
Validate before calling
try (FileSystem fs = FileSystems.newFileSystem(zipPath)) {
try (Stream<Path> s = Files.list(fs.getPath("/"))) {
List<Path> top = s.toList();
boolean ok = top.size() == 1 && Files.isDirectory(top.get(0))
&& Files.exists(top.get(0).resolve("level.dat"));
if (!ok) throw new IllegalArgumentException("Zip must contain exactly one world folder with level.dat");
}
} Type guard
static boolean isValidWorldZip(Path zip) {
try (FileSystem fs = FileSystems.newFileSystem(zip)) {
try (Stream<Path> s = Files.list(fs.getPath("/"))) {
List<Path> top = s.toList();
return top.size() == 1 && Files.isDirectory(top.get(0))
&& Files.exists(top.get(0).resolve("level.dat"));
}
} catch (IOException e) { return false; }
} Try / catch
try {
World w = new World(zipFile);
} catch (IOException e) {
if (e.getMessage().contains("Not a valid world zip file")) {
// extract manually or ask user to re-zip with a single world folder
} else throw e;
} Prevention
- Zip the world folder itself, not its contents at root
- Exclude __MACOSX and metadata files from archives
- Only include one world per zip
- Preview zip structure before import
When it happens
Trigger: Opening a zip whose root holds level.dat plus sibling files (screenshot zips, multi-world archives, zips made on macOS with __MACOSX folders), or a zip with multiple top-level directories.
Common situations: Users zipping multiple worlds together, browser downloading a page-wrapped zip, macOS Finder zips adding extra root entries, modpack exports with README at root.
Related errors
- Malformed datapack zip
- Not a valid world directory since level.dat or…
- Not a valid world zip file since level.dat or…
- Not a valid world directory
- World zip malformed
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/949de78f86a15b4b.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java:94
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 if (Files.isRegularFile(file))
try (FileSystem fs = CompressingUtils.readonly(this.file).setAutoDetectEncoding(true).build()) {
Path root;
if (Files.isRegularFile(fs.getPath("/level.dat"))) {
root = fs.getPath("/");
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)) {View on GitHub (pinned to 24702dc5a0)