HMCL-dev/HMCL · error · IllegalArgumentException

Json-Patch isn't a file:

Error message

Json-Patch isn't a file: 

What it means

Each component's local patch is expected at patches/<uid>.json inside the modpack. If that path exists but is not a regular file (e.g. a directory named like the patch inside the extracted/zip filesystem), HMCL refuses to read it rather than failing later with a confusing I/O error.

Solutions

  1. Open the modpack zip and replace the patches/<uid>.json directory with a proper JSON patch file.
  2. Re-download or re-export the modpack archive.
  3. Remove the bogus entry so HMCL fetches the patch from the component metadata server instead.

Example fix

// inside modpack zip
// before: patches/net.minecraft/          (directory)
// after:  patches/net.minecraft.json      (regular file)
Defensive patterns

Strategy: validation

Validate before calling

Path p = root.resolve("patches/" + uid + ".json");
boolean usable = Files.isRegularFile(p);

Try / catch

try { task.run(); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith("Json-Patch isn't a file")) { /* repack the modpack */ } }

Prevention

When it happens

Trigger: preExecute resolves patches/<uid>.json inside the modpack filesystem; Files.exists is true but Files.isRegularFile is false, typically because a directory with that name exists in the archive.

Common situations: A malformed or hand-modified modpack zip where patches/ contains directories instead of JSON files; filesystem quirks after re-zipping the pack.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at HMCLCore/src/main/java/org/jackhuang/hmcl/modpack/multimc/MultiMCModpackInstallTask.java:242

            String mcVersion = null;
            for (MultiMCManifest.MultiMCManifestComponent component : components) {
                if (MultiMCComponents.getComponent(component.getUid()) == GameComponentType.GAME) {
                    mcVersion = component.getVersion();
                    break;
                }
            }
            if (mcVersion == null) {
                throw new IllegalStateException("Cannot load modpacks without Minecraft.");
            }

            for (MultiMCManifest.MultiMCManifestComponent component : components) {
                String componentID = Objects.requireNonNull(component.getUid(), "Component ID");
                Path patchPath = root.resolve(String.format("patches/%s.json", componentID));

                if (Files.exists(patchPath)) {
                    if (!Files.isRegularFile(patchPath)) {
                        throw new IllegalArgumentException("Json-Patch isn't a file: " + componentID);
                    }

                    MultiMCInstancePatch patch = MultiMCInstancePatch.read(componentID, Files.readString(patchPath));
                    patches.add(Task.supplyAsync(() -> patch)); // TODO: Task.completed has unclear compatibility issue.
                } else {
                    patches.add(
                            new GetTask(MultiMCComponents.getMetaURL(componentID, component.getVersion(), mcVersion))
                                    .thenApplyAsync(s -> MultiMCInstancePatch.read(componentID, s))
                    );
                }
            }
            dependents.add(new MMCInstancePatchesAssembleTask(patches, mcVersion));
        }
    }

    private static final class MMCInstancePatchesAssembleTask extends Task<List<MultiMCInstancePatch>> {
        private final List<Task<MultiMCInstancePatch>> patches;
        private final String mcVersion;

View on GitHub (pinned to 24702dc5a0)