HMCL-dev/HMCL · error · IOException

Not a valid world directory since level.dat or…

Error message

Not a valid world directory since level.dat or special_level.dat cannot be found.

What it means

World's constructor validates that the directory is a real Minecraft save by looking for level.dat, falling back to special_level.dat (used by the 20w14infinite snapshot). If neither exists it throws IOException, since world metadata cannot be read.

Solutions

  1. Verify the target path is an actual world directory containing level.dat
  2. Restore level.dat from backup or re-extract the world zip
  3. Move up/down one directory level (user may have selected the wrong folder)
  4. Re-download the world from its source

Example fix

// before
new World("saves/")            // selects the saves root, no level.dat
// after
new World("saves/MyWorld")     // actual world containing level.dat
Defensive patterns

Strategy: validation

Validate before calling

Path p = Path.of(dir);
if (!Files.isDirectory(p) || !(Files.exists(p.resolve("level.dat")) || Files.exists(p.resolve("special_level.dat")))) {
    throw new IllegalArgumentException("Not a Minecraft world directory: " + dir);
}

Type guard

static boolean looksLikeWorldDir(Path p) {
    return Files.isDirectory(p)
        && (Files.isRegularFile(p.resolve("level.dat")) || Files.isRegularFile(p.resolve("special_level.dat")));
}

Try / catch

try {
    World w = new World(dir);
} catch (IOException e) {
    if (e.getMessage().contains("level.dat or special_level.dat")) {
        // tell user to select a valid world folder
    } else throw e;
}

Prevention

When it happens

Trigger: Calling new World(path) on a directory that lacks both level.dat and special_level.dat.

Common situations: Pointing the launcher at a folder that is not a save (e.g. backups dir, 'saves' root itself), partially deleted/corrupted world, unzip that stripped dotfiles, modpack folders mislabeled as worlds.

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


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/game/World.java:69

    private CompoundTag worldGenSettingsDataBackingTag; // Use for writing back to the file
    private CompoundTag normalizedWorldGenSettingsData; // Use for reading/modification
    private Path worldGenSettingsDataPath;

    private CompoundTag playerData; // Use for both reading/modification and writing back to the file
    private Path playerDataPath;

    public World(Path file) throws IOException {
        this.file = file;

        if (Files.isDirectory(file)) {
            fileName = FileUtils.getName(this.file);
            Path levelDatPath = this.file.resolve("level.dat");
            if (!Files.exists(levelDatPath)) { // version 20w14infinite
                levelDatPath = this.file.resolve("special_level.dat");
            }
            if (!Files.exists(levelDatPath)) {
                throw new IOException("Not a valid world directory since level.dat or special_level.dat cannot be found.");
            }
            this.levelDataPath = levelDatPath;
            loadAndCheckWorldData();

            Path iconFile = this.file.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 if (Files.isRegularFile(file))
            try (FileSystem fs = CompressingUtils.readonly(this.file).setAutoDetectEncoding(true).build()) {
                Path root;
                if (Files.isRegularFile(fs.getPath("/level.dat"))) {

View on GitHub (pinned to 24702dc5a0)