HMCL-dev/HMCL · error · WorldLockedException

The world has been locked

Error message

The world  has been locked

What it means

World.delete() refuses to delete a Minecraft save directory while a session.lock file lock is held on it. HMCL guards deletion this way so it never yanks the world out from under a running game process that has the world open. It throws WorldLockedException (an IOException subclass) carrying the world directory path.

Solutions

  1. Close the running Minecraft instance that has this world open, then retry delete()
  2. Check for orphaned Java processes still holding session.lock and kill them (lsof/handle.exe)
  3. Close other programs (antivirus scan, backup sync) holding session.lock open and retry
  4. Catch WorldLockedException and surface a 'world is in use' message instead of retrying delete

Example fix

// before
world.delete();
// after
try {
    world.delete();
} catch (WorldLockedException e) {
    UI.warn("World is in use by a running game; close it first.");
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (world.isLocked()) {
    throw new IllegalStateException("World is in use; close the game first");
}
world.delete();

Try / catch

try {
    world.delete();
} catch (WorldLockedException e) {
    notifyUserWorldInUse(world.getFile().toString());
}

Prevention

When it happens

Trigger: Calling World.delete() on a world whose session.lock is currently locked by another process (typically a running Minecraft instance), i.e. World.isLocked() returns true at the moment of the call.

Common situations: User tries to delete a world from the launcher while that world's Minecraft instance is still running or crashed without releasing its lock; an orphaned JVM holds the file lock; on Windows the file is also held open by an antivirus or backup tool.

Related errors


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

Appendix: source

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

            }
            new World(worldDir).rename(name);
        } else if (Files.isDirectory(file)) {
            FileUtils.copyDirectory(file, worldDir);
        }
    }

    public void export(Path zip, String worldName) throws IOException {
        if (!Files.isDirectory(file))
            throw new IOException();

        try (Zipper zipper = new Zipper(zip)) {
            zipper.putDirectory(file, worldName);
        }
    }

    public void delete() throws IOException {
        if (isLocked()) {
            throw new WorldLockedException("The world " + getFile() + " has been locked");
        }
        FileUtils.forceDelete(file);
    }

    public void copy(String newName) throws IOException {
        if (!Files.isDirectory(file)) {
            throw new IOException("Not a valid world directory");
        }

        if (isLocked()) {
            throw new WorldLockedException("The world " + getFile() + " has been locked");
        }

        Path newPath = file.resolveSibling(newName);
        FileUtils.copyDirectory(file, newPath, path -> !path.contains("session.lock"));
        World newWorld = new World(newPath);
        newWorld.rename(newName);
    }

View on GitHub (pinned to 24702dc5a0)