HMCL-dev/HMCL · warning · IOException

Failed to close session lock channel of the world

Error message

Failed to close session lock channel of the world 

What it means

WorldManageUIUtils.closeSessionLockChannel closes a world's session.lock FileChannel and wraps any IOException from close() in a new IOException prefixed with "Failed to close session lock channel of the world ". Note the message string concatenates world.getFile() (the path) even though the log line uses getFileName(). It indicates the lock channel could not be released, which usually means the OS-level close failed.

Solutions

  1. Retry the delete/export operation after ensuring no other process (game, editor, sync client) is touching the world folder
  2. Check the chained cause (e.getCause()) to identify the real OS/filesystem error
  3. If developing, log-and-continue may be acceptable since close() failures are often benign — consider not failing the whole operation
  4. Run disk/antivirus exclusion checks on the .minecraft/saves directory

Example fix

// before
throw new IOException("Failed to close session lock channel of the world " + world.getFile(), e);
// after
try {
    sessionLockChannel.close();
} catch (IOException e) {
    LOG.warning("Failed to close session lock channel of the world " + world.getFile(), e);
    // non-fatal: proceed with delete/export
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before delete/export
if (Files.exists(worldDir.resolve("session.lock"))) {
    try (FileChannel ignored = FileChannel.open(worldDir.resolve("session.lock"), WRITE, CREATE_NEW)) {
        // if this succeeds no other process holds an OS lock
    } catch (IOException e) {
        throw new IllegalStateException("World in use by another process");
    }
}

Try / catch

try {
    WorldManageUIUtils.delete(world);
} catch (IOException e) {
    if (e.getMessage().startsWith("Failed to close session lock channel")) {
        LOG.warning("Non-fatal lock close failure", e); // proceed or retry
    } else throw e;
}

Prevention

When it happens

Trigger: Called from World deletion or export flows after getSessionLockChannel opened a session.lock file; the FileChannel.close() call throws IOException (e.g. the underlying file descriptor is in a bad state or the filesystem errors on close).

Common situations: Deleting/exporting a Minecraft world on Windows while another process holds the lock; antivirus or backup software interfering with file handles; network or FAT/exFAT filesystems with flaky close semantics; a previous double-close leaving the channel invalid.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/WorldManageUIUtils.java:127

                            ).whenComplete(Schedulers.javafx(), (throwable) -> {
                                if (throwable == null) {
                                    handler.resolve();
                                } else {
                                    handler.reject(i18n("world.duplicate.failed"));
                                    LOG.warning("Failed to duplicate world " + world.getFile(), throwable);
                                }
                            })
                            .start();
                }, "", new RequiredValidator(), new Validator(i18n("world.duplicate.failed.invalid_name"), FileUtils::isNameValid));
    }

    public static void closeSessionLockChannel(World world, FileChannel sessionLockChannel) throws IOException {
        if (sessionLockChannel != null) {
            try {
                sessionLockChannel.close();
                LOG.info("Closed session lock channel of the world " + world.getFileName());
            } catch (IOException e) {
                throw new IOException("Failed to close session lock channel of the world " + world.getFile(), e);
            }
        }
    }

    public static FileChannel getSessionLockChannel(World world) {
        try {
            FileChannel lock = world.lock();
            LOG.info("Acquired lock on world " + world.getFileName());
            return lock;
        } catch (WorldLockedException ignored) {
            return null;
        }
    }

}

View on GitHub (pinned to 24702dc5a0)