HMCL-dev/HMCL · error · IOException
Primary JAR source is not a regular file
Error message
Primary JAR source is not a regular file: ${normalizedSource} What it means
After confirming the instance exists, putPrimaryJar() normalizes the source path and requires it to be a regular file on disk. If not, it throws IOException('Primary JAR source is not a regular file: <path>'). This guards against committing directories, symlinks to nothing, or nonexistent paths as an instance's primary jar.
Solutions
- Check Files.isRegularFile(normalizedPath) before calling putPrimaryJar and download/extract the jar if missing
- Ensure the source path is absolute and points at the actual .jar file, not its containing directory
- Catch this IOException and report the exact path to the user so they can fix the source location
- Fix any producer step (download/copy) so it completes before staging the primary jar
Example fix
// before
draft.putPrimaryJar(instanceId, Paths.get("downloads", "mc.jar"));
// after
Path jar = Paths.get("downloads", "mc.jar").toAbsolutePath().normalize();
if (!Files.isRegularFile(jar)) {
throw new IOException("Missing primary jar, run download first: " + jar);
}
draft.putPrimaryJar(instanceId, jar); Defensive patterns
Strategy: validation
Validate before calling
Path src = source.toAbsolutePath().normalize();
if (!Files.isRegularFile(src)) {
throw new IllegalArgumentException("Not a jar file: " + src);
} Try / catch
try {
draft.putPrimaryJar(instanceId, source);
} catch (IOException e) {
LOG.warning("Primary jar source invalid: " + e.getMessage());
} Prevention
- Download/extract the jar fully before staging it
- Always pass absolute, normalized paths
- Check Files.isRegularFile (not just exists) — directories pass exists()
When it happens
Trigger: Passing a directory, a dangling symlink, or a path that does not exist (e.g. jar not yet downloaded/extracted) as the source to putPrimaryJar; also passing a relative path that resolves outside the expected location after normalization.
Common situations: Automated flows that assume a downloaded jar exists without checking; copying install scripts between machines where the jar lives at a different path; races where a temp jar is deleted before commit.
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
- Theme pack directory does not contain
- Theme pack does not contain
- Missing release file
- File is not a Fabric mod.
- "File " + modFile + " is not a Forge mod."
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/a62096a2b380369e.
Report an issue: GitHub.
Appendix: source
Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/game/DefaultGameRepositoryDraft.java:134
/// {@inheritDoc}
@Override
public void put(GameInstanceManifest manifest) throws IOException {
checkOpen();
putManifest(manifest, true);
}
/// {@inheritDoc}
@Override
public void putPrimaryJar(GameInstanceID instanceId, Path source) throws IOException {
checkOpen();
if (!manifests.containsKey(instanceId)) {
throw new NoSuchGameInstanceException(instanceId);
}
Path normalizedSource = source.toAbsolutePath().normalize();
if (!Files.isRegularFile(normalizedSource)) {
throw new IOException("Primary JAR source is not a regular file: " + normalizedSource);
}
Path target = getPrimaryJarTarget(instanceId);
if (normalizedSource.equals(target)) {
primaryJarSources.remove(instanceId);
} else {
primaryJarSources.put(instanceId, normalizedSource);
}
}
/// {@inheritDoc}
@Override
public void remove(GameInstanceID instanceId) {
checkOpen();
if (manifests.remove(instanceId) == null) {
throw new NoSuchGameInstanceException(instanceId);
}
View on GitHub (pinned to 24702dc5a0)