HMCL-dev/HMCL · error · IOException

OptiFine patcher failed, command

Error message

OptiFine patcher failed, command: ${command}

What it means

Thrown when the external OptiFine patcher process (java -jar invoking the OptiFine installer against the client jar) exits with a non-zero code. The IOException includes the full command line so the failing invocation can be reproduced.

Solutions

  1. Re-run with a Java version compatible with the OptiFine installer (usually Java 8+; match the game's requirement).
  2. Re-download both the client jar and OptiFine installer to rule out corruption.
  3. Run the exact command printed in the exception manually to see the patcher's own error output.
  4. Check target paths are writable and not locked by another process.

Example fix

// before
int exitCode = SystemUtils.callExternalProcess(command);
if (exitCode != 0)
    throw new IOException("OptiFine patcher failed, command: " + new CommandBuilder().addAll(Arrays.asList(command)));
// after
int exitCode = SystemUtils.callExternalProcess(command);
if (exitCode != 0)
    throw new IOException("OptiFine patcher failed (exit " + exitCode + "), command: "
        + new CommandBuilder().addAll(Arrays.asList(command)));
Defensive patterns

Strategy: try-catch

Validate before calling

Path javaBin = resolveGameJava(manifest); // ensure Java version matches OptiFine requirement
if (!Files.isRegularFile(minecraftJar) || !Files.isRegularFile(installerFile))
    throw new IllegalStateException("inputs missing");

Try / catch

try {
    await(task);
} catch (IOException e) {
    if (e.getMessage() != null && e.getMessage().contains("OptiFine patcher failed")) {
        // run the embedded command manually to see the patcher's stderr
        log.severe(e.getMessage());
    }
}

Prevention

When it happens

Trigger: SystemUtils.callExternalProcess(command) returns non-zero while running the OptiFine patch command with [minecraftJar, installerFile, optiFineLibraryPath] — e.g. wrong Java version, incompatible OptiFine/game version combo, corrupted client jar, or file locks.

Common situations: Running the patcher with a too-new/too-old JRE, OptiFine build not matching the Minecraft version, corrupted downloads, or paths with spaces/odd characters breaking the external process.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — 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/0f4d321797c29c7c. Report an issue: GitHub.

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/download/optifine/OptiFineInstallTask.java:187

        }

        // Install launch wrapper modified by OptiFine
        boolean hasLaunchWrapper = false;
        try (FileSystem fs = CompressingUtils.createReadOnlyZipFileSystem(installerFile)) {
            Path optiFineLibraryPath = gameRepository.getLayout().getLibraryFile(manifest.id(), optiFineLibrary);
            if (Files.exists(fs.getPath("optifine/Patcher.class"))) {
                String[] command = {
                        JavaRuntime.getDefault().getBinary().toString(),
                        "-cp",
                        installerFile.toString(),
                        "optifine.Patcher",
                        minecraftJar.toAbsolutePath().normalize().toString(),
                        installerFile.toString(),
                        optiFineLibraryPath.toString()
                };
                int exitCode = SystemUtils.callExternalProcess(command);
                if (exitCode != 0)
                    throw new IOException("OptiFine patcher failed, command: " + new CommandBuilder().addAll(Arrays.asList(command)));
            } else {
                FileUtils.copyFile(installerFile, optiFineLibraryPath);
            }

            try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(optiFineLibraryPath)) {
                Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml"));
            }

            Path launchWrapper2 = fs.getPath("launchwrapper-2.0.jar");
            if (Files.exists(launchWrapper2)) {
                Library launchWrapper = new Library(new Artifact("optifine", "launchwrapper", "2.0"));
                Path launchWrapperFile = gameRepository.getLayout().getLibraryFile(manifest.id(), launchWrapper);
                Files.createDirectories(launchWrapperFile.toAbsolutePath().getParent());
                FileUtils.copyFile(launchWrapper2, launchWrapperFile);
                hasLaunchWrapper = true;
                libraries.add(launchWrapper);
            }

View on GitHub (pinned to 24702dc5a0)