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

  1. Check Files.isRegularFile(normalizedPath) before calling putPrimaryJar and download/extract the jar if missing
  2. Ensure the source path is absolute and points at the actual .jar file, not its containing directory
  3. Catch this IOException and report the exact path to the user so they can fix the source location
  4. 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

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


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)