HMCL-dev/HMCL · error · FileNotFoundException

File missing:

Error message

File missing: 

What it means

Thrown when a Forge processor reports success but its declared output file is not present on disk. After each processor runs, HMCL verifies every entry in the outputs map exists; a missing file means the processor silently failed to produce a required artifact (e.g. patched jar or binpatches).

Solutions

  1. Check game-directory write permissions so the processor can create output files
  2. Verify token substitution (ROOT/MINECRAFT_JAR etc.) resolved to the expected paths in HMCL logs
  3. Delete stale processor outputs and re-run installation so processors execute cleanly
  4. Inspect processor logs to confirm which output it was supposed to produce and why it did not

Example fix

// before: running on a read-only game directory
Path base = Paths.get("/mnt/ro/minecraft");
// after: ensure the repository is writable
if (!Files.isWritable(base)) throw new IOException("Game directory must be writable: " + base);
new ForgeNewInstallTask(dependencyManager, manifest, minecraftJar, version, installer);
Defensive patterns

Strategy: validation

Validate before calling

// Ensure the game directory is writable so processors can emit outputs
Path base = gameRepository.getBaseDirectory();
if (!Files.isWritable(base)) throw new IOException("Game directory not writable: " + base);

Try / catch

try {
    installTask.run();
} catch (FileNotFoundException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("File missing:")) {
        // check permissions/token substitution; clean temp outputs and retry install
    }
}

Prevention

When it happens

Trigger: Following a successful processor execution (exit code 0), Paths.get(outputKey) is not a regular file — the processor wrote nothing or wrote to a different path because tokens (e.g. {MINECRAFT_JAR}, {ROOT}) resolved to unexpected locations.

Common situations: Processor partially ran and crashed after process exit code was misreported; permission issues preventing writes into the game directory; token mismatch causing output to a different directory; stale antivirus interference.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/download/forge/ForgeNewInstallTask.java:158

            List<String> args = new ArrayList<>(processor.getArgs().size());
            for (String arg : processor.getArgs()) {
                String parsed = parseLiteral(arg, vars);
                if (parsed == null)
                    throw new ArtifactMalformedException("Invalid forge installation configuration");
                args.add(parsed);
            }

            command.addAll(args);

            LOG.info("Executing external processor " + processor.getJar().toString() + ", command line: " + new CommandBuilder().addAll(command).toString());
            int exitCode = SystemUtils.callExternalProcess(command);
            if (exitCode != 0)
                throw new IOException("Game processor exited abnormally with code " + exitCode);

            for (Map.Entry<String, String> entry : outputs.entrySet()) {
                Path artifact = Paths.get(entry.getKey());
                if (!Files.isRegularFile(artifact))
                    throw new FileNotFoundException("File missing: " + artifact);

                String code;
                try (InputStream stream = Files.newInputStream(artifact)) {
                    code = DigestUtils.digestToString("SHA-1", stream);
                }

                if (!Objects.equals(code, entry.getValue())) {
                    if (!ZlibUtils.IS_ZLIB_COMPATIBLE && FileUtils.getExtension(artifact).equals("jar")) {
                        // Forge/NeoForge generates JARs dynamically during installation.
                        // When native compression libraries such as zlib-ng are in use,
                        // the resulting JAR may be compressed differently, causing its
                        // SHA-1 hash to differ from the expected value recorded in the
                        // install profile. In this case, fall back to verifying that the
                        // file is at least a structurally valid ZIP/JAR archive.
                        try {
                            FileDownloadTask.ZIP_INTEGRITY_CHECK_HANDLER.checkIntegrity(artifact, artifact);
                            LOG.info("Ignoring SHA-1 mismatch for " + artifact + " due to non-standard zlib compression output");
                            continue;

View on GitHub (pinned to 24702dc5a0)