HMCL-dev/HMCL · error · IOException

Launcher config path

Error message

Launcher config path 

What it means

SettingsManager checks that the launcher config directory is writable before saving. When a writability check fails but the location looks like a Samba share (or the OpenJDK canWrite bug applies), it throws SambaException instead of silently failing, since proceeding could cause data loss.

Solutions

  1. Move the HMCL data/config directory to a local writable path (e.g. launch with --hmcl-dir pointing elsewhere)
  2. Ensure the Samba share is mounted read-write and the user has write permission
  3. Remount or reconfigure the share so writability detection succeeds
  4. Catch SambaException in the launcher UI and warn the user to relocate the config

Example fix

// before
hmcl --workingdir /mnt/samba/hmcl
// after
hmcl --hmcl-dir ~/.local/share/hmcl
Defensive patterns

Strategy: try-catch

Validate before calling

Path loc = Paths.get(location); if (!Files.isWritable(loc)) { LOG.warning("Config location not writable: " + loc); }

Try / catch

try { settingsManager.checkWritable(); } catch (SambaException e) { LOG.warning("Config on Samba share; writability may be misdetected", e); } catch (IOException e) { /* relocate config */ }

Prevention

When it happens

Trigger: Placing the HMCL config path on a Samba/network share (or a filesystem where File.canWrite() misreports false) and then triggering a settings save.

Common situations: Config on a Windows SMB share or NAS mount; OpenJDK canWrite() false negatives on certain filesystems; read-only mounts.

Understand the failure class

Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/setting/SettingsManager.java:1659

            }
        } catch (IOException e) {
            LOG.warning("Failed to get owner");
        }
    }

    /// Checks that the given config path is writable.
    private static void checkWritable(Path location) throws IOException {
        if (!Files.isWritable(location)) {
            if (OperatingSystem.CURRENT_OS == OperatingSystem.WINDOWS
                    && location.getFileSystem() == FileSystems.getDefault()
                    && location.toFile().canWrite()) {
                LOG.warning("Launcher config path " + location + " is not writable, but it seems to be a Samba share or OpenJDK bug");
                // There are some serious problems with the implementation of Samba or OpenJDK
                throw new SambaException();
            } else {
                // the config cannot be saved
                // throw up the error now to prevent further data loss
                throw new IOException("Launcher config path " + location + " is not writable");
            }
        }
    }

    /// Loads user settings and installs the save listener.
    ///
    /// @return the user settings file access status
    private static SettingFileAccess loadUserSettings(
            @Nullable LegacyConfigMigrator.UserSettingsMigrationResult migrationResult) throws IOException {
        if (userSettingsInstance != null) {
            throw new IllegalStateException("User settings are already loaded");
        }

        boolean newlyCreated = !Files.exists(USER_SETTINGS_LOCATION);
        @Nullable UserSettings migratedUserSettings = newlyCreated && migrationResult != null
                ? migrationResult.userSettings()
                : null;
        JsonSettingFile.LoadResult<UserSettings> result = USER_SETTINGS_FILE.load(migratedUserSettings);

View on GitHub (pinned to 24702dc5a0)