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
- Check game-directory write permissions so the processor can create output files
- Verify token substitution (ROOT/MINECRAFT_JAR etc.) resolved to the expected paths in HMCL logs
- Delete stale processor outputs and re-run installation so processors execute cleanly
- 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
- Install into a writable game directory (avoid read-only mounts)
- Do not run two installations into the same directory concurrently
- Clear stale temp/processor outputs before retrying
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
- Game processor file not found, should be downloaded in…
- SHA-1
- Theme pack directory does not contain
- Theme pack does not contain
- Missing release file
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)