HMCL-dev/HMCL · error · IOException

level.dat missing LastPlayed

Error message

level.dat missing LastPlayed

What it means

Thrown by loadAndCheckLevelData when level.dat's Data compound lacks a 'LastPlayed' LongTag. HMCL displays and sorts worlds by their last-played timestamp, so a missing or wrong-typed LastPlayed makes the world data incomplete.

Solutions

  1. Set Data.LastPlayed as a LongTag (epoch milliseconds) with an NBT editor
  2. Restore level.dat from backup
  3. Re-copy the world from a working Minecraft instance
  4. Fix test/generator code to write LastPlayed with putLong

Example fix

// before
dataTag.putString("LastPlayed", "1700000000000"); // wrong type
// after
dataTag.putLong("LastPlayed", 1700000000000L);
Defensive patterns

Strategy: try-catch

Try / catch

try {
    World world = new World(worldDir);
} catch (IOException e) {
    if (String.valueOf(e.getMessage()).equals("level.dat missing LastPlayed")) {
        // set LastPlayed via NBT editor or treat as unsupported world
    }
}

Prevention

When it happens

Trigger: new World(worldDir) on a level.dat where Data.LastPlayed is absent or stored as a non-Long tag (e.g. Int or String).

Common situations: level.dat hand-edited or rebuilt by third-party tools omitting LastPlayed, corrupted metadata after a crash, minimal hand-crafted NBT fixtures in tests, or conversions between world formats.

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/0263605ffe891609. Report an issue: GitHub.

Appendix: source

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

    public static boolean supportQuickPlay(GameVersionNumber gameVersionNumber) {
        return gameVersionNumber != null && gameVersionNumber.isAtLeast("1.20", "23w14a");
    }

    private void loadAndCheckWorldData() throws IOException {
        loadAndCheckLevelData(levelDataPath);
        loadOtherData();
    }

    private void loadAndCheckLevelData(Path levelDat) throws IOException {
        this.levelData = NBTCodec.of().readTag(levelDat, TagType.COMPOUND);
        if (!(levelData.get("Data") instanceof CompoundTag data))
            throw new IOException("level.dat missing Data");

        if (!(data.get("LevelName") instanceof StringTag))
            throw new IOException("level.dat missing LevelName");

        if (!(data.get("LastPlayed") instanceof LongTag))
            throw new IOException("level.dat missing LastPlayed");
        this.dataTag = data;
    }

    private void loadOtherData() throws IOException {
        if (!(levelData.get("Data") instanceof CompoundTag data)) return;

        Path worldGenSettingsDatPath = file.resolve("data/minecraft/world_gen_settings.dat");
        if (data.get("WorldGenSettings") instanceof CompoundTag worldGenSettingsTag) {
            setWorldGenSettingsData(null, worldGenSettingsTag, worldGenSettingsTag);
        } else if (Files.isRegularFile(worldGenSettingsDatPath)) {
            CompoundTag raw = NBTCodec.of().readTag(worldGenSettingsDatPath, TagType.COMPOUND);
            if (raw.get("data") instanceof CompoundTag compoundTag) {
                setWorldGenSettingsData(worldGenSettingsDatPath, raw, compoundTag);
            } else {
                setWorldGenSettingsData(null, null, null);
            }
        } else {
            setWorldGenSettingsData(null, null, null);

View on GitHub (pinned to 24702dc5a0)