HMCL-dev/HMCL · error · IllegalArgumentException

The extension of is not 'bat' or 'ps1' in Windows

Error message

The extension of  is not 'bat' or 'ps1' in Windows

What it means

DefaultLauncher.makeLaunchScript writes a launch script and requires, on Windows, a .bat extension (unless PowerShell .ps1 was selected). It throws IllegalArgumentException("The extension of <file> is not 'bat' or 'ps1' in Windows") when the script file path on a Windows host has any other extension, because the generated script format would not be runnable.

Solutions

  1. Use a .bat file name when exporting the launch script on Windows (or .ps1 for PowerShell).
  2. Rename the target script file's extension to .bat before calling makeLaunchScript.
  3. On a Windows machine that needs a shell script, export .ps1 instead of .sh.
  4. Pick the export format according to the current OS rather than a fixed path.

Example fix

// before
// launcher.makeLaunchScript(Path.of("game.sh"), ...); // on Windows
// after
// launcher.makeLaunchScript(Path.of("game.bat"), ...); // on Windows
Defensive patterns

Strategy: validation

Validate before calling

String ext = FileUtils.getExtension(scriptFile);
boolean isWindows = OS.WINDOWS == OperatingSystem.CURRENT_OS;
if (isWindows && !"ps1".equals(ext) && !"bat".equalsIgnoreCase(ext)) {
    throw new IllegalArgumentException("Use .bat or .ps1 on Windows, got: " + ext);
}

Try / catch

try {
    launcher.makeLaunchScript(scriptFile, ...);
} catch (IllegalArgumentException e) {
    // rename the script file to .bat or .ps1 and retry
}

Prevention

When it happens

Trigger: Calling makeLaunchScript(scriptFile, ...) on Windows with a file whose extension is not bat/ps1 — e.g. saving a launch script as .sh or .txt while running on Windows.

Common situations: Users exporting a script to share with Linux friends while on Windows; saving with a .txt extension added by a text editor; automation passing a fixed filename without checking OS.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/launch/DefaultLauncher.java:871

                // With ModLauncher, OptiFine is discovered via HMCLTransformerDiscoveryService, not classpath.
                !GameComponentAnalyzer.MOD_LAUNCHER_MAIN.equals(manifest.mainClass())) {
            classpath.add(FileUtils.getAbsolutePath(selectedOptiFineInstallerFile));
        }
        return classpath;
    }

    @Override
    public void makeLaunchScript(Path scriptFile) throws IOException {
        boolean isWindows = OperatingSystem.WINDOWS == OperatingSystem.CURRENT_OS;

        Path nativeFolder = getNativeFolder();

        String scriptExtension = FileUtils.getExtension(scriptFile);
        boolean usePowerShell = "ps1".equals(scriptExtension);

        if (!usePowerShell) {
            if (isWindows && !scriptExtension.equalsIgnoreCase("bat"))
                throw new IllegalArgumentException("The extension of " + scriptFile + " is not 'bat' or 'ps1' in Windows");
            else if (!isWindows && !(scriptExtension.equalsIgnoreCase("sh") || scriptExtension.equalsIgnoreCase("command") || scriptExtension.equalsIgnoreCase("bash")))
                throw new IllegalArgumentException("The extension of " + scriptFile + " is not 'sh', 'bash', 'ps1' or 'command' in macOS/Linux");
        }

        final Command commandLine = generateCommandLine(nativeFolder);
        final String command = usePowerShell ? null : commandLine.commandLine.toString();
        Map<String, String> envVars = getEnvVars(nativeFolder);

        if (isWindows && !usePowerShell) {
            // https://stackoverflow.com/a/28452546
            // https://learn.microsoft.com/troubleshoot/windows-client/shell-experience/command-line-string-limitation
            if (command.length() > 32767) {
                throw new CommandTooLongException();
            }
        }

        if (isUsingLog4j())
            extractLog4jConfigurationFile();

View on GitHub (pinned to 24702dc5a0)