HMCL-dev/HMCL · error · IOException
Primary JAR source is not a regular file
Error message
Primary JAR source is not a regular file: ${source} What it means
applyPrimaryJar validates that the source JAR to install as the instance's primary jar is a regular file before copying/backing it up. The library throws IOException when the given source path is missing, a directory, or otherwise not a normal file. This protects commit() from copying directories or special files into the instance's jar slot.
Solutions
- Verify the source with Files.isRegularFile(source) before committing
- Re-download or re-install the version jar so the file actually exists
- Fix the manifest/version JSON jar path to point to the real jar file
- If the jar is produced by a prior step, make sure that step succeeded and produced a regular file
Example fix
// before
repo.commit(draft); // source jar path from config is a directory
// after
Path jar = Paths.get(config.jarPath());
if (Files.isRegularFile(jar)) {
repo.commit(draft);
} else {
throw new IllegalStateException("Not a jar file: " + jar);
} Defensive patterns
Strategy: validation
Validate before calling
if (!Files.isRegularFile(source)) throw new IllegalStateException("Primary jar missing: " + source); Type guard
static boolean isJarFile(Path p) {
return Files.isRegularFile(p) && p.getFileName().toString().endsWith(".jar");
} Try / catch
try { repo.commit(draft); }
catch (IOException e) { if (e.getMessage().startsWith("Primary JAR source is not a regular file")) { redownloadJar(id); } else throw e; } Prevention
- Verify the jar exists and is a regular file after every download step
- Keep jar paths relative to the instance root and derive them from the layout
- Fix version JSON jar entries that point at directories or stale paths
When it happens
Trigger: commit() calling applyPrimaryJar with a source path that does not exist, points to a directory, or is a device/symlink-to-non-regular-file — commonly a downloaded or version jar path that is wrong.
Common situations: Failed or partial game download left no jar; version JSON points at a directory or wrong filename; user manually replaced the jar with a folder; network installer produced an empty path.
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
- Cannot export a background directory as a theme-pack asset
- Theme background image does not exist
- Instance directory does not exist
- Not a valid world directory
- Failed to remove instance before restoring backup:
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/1c69fda84a746dc5.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepositoryDraft.java:565
applied.add(new AppliedFile(target, backup));
Files.writeString(target, json);
}
/// Copies a completed primary JAR into its permanent instance location while retaining a
/// rollback copy of an existing target.
///
/// @param id the instance receiving the JAR
/// @param source the completed source JAR
/// @param rollbackDirectory the directory holding rollback files
/// @param applied rollback records for files already changed
/// @throws IOException if the source or target cannot be read or written
private void applyPrimaryJar(
GameInstanceID id,
Path source,
Path rollbackDirectory,
List<AppliedFile> applied) throws IOException {
if (!Files.isRegularFile(source)) {
throw new IOException("Primary JAR source is not a regular file: " + source);
}
Path target = getPrimaryJarTarget(id);
Files.createDirectories(target.getParent());
@Nullable Path backup = backupFile(target, rollbackDirectory, "jar-", ".jar");
applied.add(new AppliedFile(target, backup));
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
}
/// Moves an existing target into rollback storage.
///
/// @param target the file about to be replaced
/// @param rollbackDirectory the directory holding rollback files
/// @param prefix the backup file prefix
/// @param suffix the backup file suffix
/// @return the backup path, or `null` when the target did not exist
/// @throws IOException if the target cannot be backed up
private static @Nullable Path backupFile(View on GitHub (pinned to 24702dc5a0)