{"record":{"id":"f964676428fc4006","repo":"HMCL-dev/HMCL","slug":"not-a-valid-world-zip-file-since-level-dat-or-spec","errorCode":null,"errorMessage":"Not a valid world zip file since level.dat or special_level.dat cannot be found.","messagePattern":"Not a valid world zip file since level\\.dat or special_level\\.dat cannot be found\\.","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java","lineNumber":107,"sourceCode":"                    fileName = FileUtils.getName(this.file);\n                } else {\n                    try (Stream<Path> filesStream = Files.list(fs.getPath(\"/\"))) {\n                        List<Path> files = filesStream.toList();\n                        if (files.size() != 1 || !Files.isDirectory(files.get(0))) {\n                            throw new IOException(\"Not a valid world zip file\");\n                        }\n\n                        root = files.get(0);\n                        fileName = FileUtils.getName(root);\n                    }\n                }\n\n                Path levelDat = root.resolve(\"level.dat\");\n                if (!Files.exists(levelDat)) { //version 20w14infinite\n                    levelDat = root.resolve(\"special_level.dat\");\n                }\n                if (!Files.exists(levelDat)) {\n                    throw new IOException(\"Not a valid world zip file since level.dat or special_level.dat cannot be found.\");\n                }\n                loadAndCheckLevelData(levelDat);\n\n                Path iconFile = root.resolve(\"icon.png\");\n                if (Files.isRegularFile(iconFile)) {\n                    try (InputStream inputStream = Files.newInputStream(iconFile)) {\n                        icon = new Image(inputStream, 64, 64, true, false);\n                        if (icon.isError())\n                            throw icon.getException();\n                    } catch (Exception e) {\n                        LOG.warning(\"Failed to load world icon\", e);\n                    }\n                }\n            }\n        else\n            throw new IOException(\"Path \" + file + \" cannot be recognized as a Minecraft world\");\n    }\n","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nWorld world = new World(Path.of(\"download.zip\")); // zip has no level.dat\n// after\ntry (FileSystem fs = CompressingUtils.readonly(Path.of(\"download.zip\")).build()) {\n    if (!Files.exists(fs.getPath(\"/level.dat\"))) throw new IllegalArgumentException(\"zip lacks level.dat\");\n}\nWorld world = new World(Path.of(\"download.zip\"));","handlingStrategy":"validation","validationCode":"static boolean looksLikeWorldZip(Path zip) throws IOException {\n    try (FileSystem fs = CompressingUtils.readonly(zip).setAutoDetectEncoding(true).build()) {\n        if (Files.isRegularFile(fs.getPath(\"/level.dat\")) || Files.isRegularFile(fs.getPath(\"/special_level.dat\"))) return true;\n        try (Stream<Path> s = Files.list(fs.getPath(\"/\"))) {\n            List<Path> top = s.toList();\n            return top.size() == 1 && Files.isDirectory(top.get(0))\n                && (Files.isRegularFile(top.get(0).resolve(\"level.dat\")) || Files.isRegularFile(top.get(0).resolve(\"special_level.dat\")));\n        }\n    }\n}","typeGuard":"static boolean isWorldZip(Path p) {\n    return Files.isRegularFile(p) && p.toString().endsWith(\".zip\");\n}","tryCatchPattern":"try {\n    World world = new World(zipPath);\n} catch (IOException e) {\n    if (String.valueOf(e.getMessage()).contains(\"Not a valid world zip\")) {\n        // surface 'archive has no level.dat' to the user and abort import\n    }\n}","preventionTips":["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"],"tags":["minecraft","io","world-import","zip"],"backgroundTag":"file-not-found","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}