HMCL-dev/HMCL · error · IOException

Cannot fix optifine

Error message

Cannot fix optifine

What it means

GameLibrariesTask.execute() wraps an IOException thrown while repairing a known broken Forge+OptiFine combination: it opens the library JAR as a writable zip filesystem and deletes META-INF/mods.toml, and if that zip manipulation fails it rethrows as 'Cannot fix optifine'. The failure means the OptiFine library file could not be patched in place.

Solutions

  1. Close any program holding the OptiFine JAR open (IDE, file manager, antivirus scan) and retry.
  2. Delete the OptiFine library file from the libraries folder and let HMCL re-download it.
  3. Check file write permissions on the libraries directory.
  4. Remove the OptiFine dependency or update Forge outside the broken range.

Example fix

// before: relying on HMCL to patch the broken pair
optifineLib = detectOptiFine();
installLibraries(...); // may throw 'Cannot fix optifine'
// after: pre-check and re-download a corrupt JAR
if (isOptiFinePair(forgeVersion) && !isUsableZip(optifineFile)) {
    Files.delete(optifineFile); // force fresh download before task runs
}
installLibraries(...);
Defensive patterns

Strategy: try-catch

Validate before calling

if (forgeVersion != null && FORGE_OPTIFINE_BROKEN_RANGE.contains(VersionNumber.asVersion(forgeVersion)) && !isWritable(file)) closeLockingProcesses(file);

Type guard

boolean isWritableJar(Path p) throws IOException { return Files.isRegularFile(p) && Files.isWritable(p); }

Try / catch

try { await(librariesTask); } catch (IOException e) { if (e.getMessage().equals("Cannot fix optifine")) { Files.delete(optifineLib); await(librariesTask); } else throw e; }

Prevention

When it happens

Trigger: The manifest declares Forge within FORGE_OPTIFINE_BROKEN_RANGE (around 1.20.4) together with OptiFine, and CompressingUtils.createWritableZipFileSystem(file) or Files.deleteIfExists throws — e.g. the file is not a zip, is read-only, or is locked by another process.

Common situations: OptiFine JAR corrupted during download, JAR opened/locked by another program (or antivirus on Windows), read-only file permissions, or the path points to a non-zip file.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameLibrariesTask.java:174

                                    .injectURLWithCandidates(fmlLib.downloadUrl());
                            dependencies.add(new FileDownloadTask(uris, file)
                                    .withCounter("hmcl.install.libraries"));
                        }
                    }
                }
            }

            Path file = gameRepository.getLayout().getLibraryFile(manifest.id(), library);
            if ("optifine".equals(library.groupId()) && Files.exists(file)) {
                if (Files.exists(file) && libraries.stream().filter(it -> it.is("optifine", "OptiFine"))
                        .anyMatch(it -> it.version().startsWith("1.20.4_"))) {
                    @Nullable String forgeVersion = GameComponentAnalyzer.analyze(manifest, GameVersionNumber.asGameVersion("1.20.4"))
                            .getVersion(GameComponentType.FORGE);
                    if (forgeVersion != null && GameComponentAnalyzer.FORGE_OPTIFINE_BROKEN_RANGE.contains(VersionNumber.asVersion(forgeVersion))) {
                        try (FileSystem fs2 = CompressingUtils.createWritableZipFileSystem(file)) {
                            Files.deleteIfExists(fs2.getPath("/META-INF/mods.toml"));
                        } catch (IOException e) {
                            throw new IOException("Cannot fix optifine", e);
                        }
                    }
                }
            } else if (library.is("org.jackhuang.hmcl", "mmc-bootstrap")) {
                if (!Files.exists(file)) {
                    try (InputStream input = Objects.requireNonNull(
                            GameLibrariesTask.class.getResourceAsStream(
                                    "/assets/game/HMCLMultiMCBootstrap-1.0.jar"),
                            "Bundled HMCLMultiMCBootstrap is missing.")) {
                        Files.createDirectories(file.getParent());
                        Files.copy(input, file, StandardCopyOption.REPLACE_EXISTING);
                    }
                }
            } else if (library.is("org.jackhuang.hmcl", "transformer-discovery-service")) {
                try (InputStream input = Objects.requireNonNull(
                        GameLibrariesTask.class.getResourceAsStream(
                                "/assets/game/HMCLTransformerDiscoveryService-1.0.jar"),
                        "Bundled HMCLTransformerDiscoveryService is missing.")) {

View on GitHub (pinned to 24702dc5a0)