HMCL-dev/HMCL · error · IllegalArgumentException

Missing game manifest

Error message

Missing game manifest

What it means

Instances.installFromJson reads a GameInstanceManifest JSON file to install a Minecraft game instance. If JsonUtils.fromJsonFile returns null (empty or whitespace-only file that parses to no value), it throws IllegalArgumentException('Missing game manifest'); the method catches it, logs a warning, shows a 'malformed JSON' dialog, and aborts the install.

Solutions

  1. Ensure the file contains a complete GameInstanceManifest JSON object (non-empty, with formatVersion etc.).
  2. Check the file size on disk — recreate/re-download the manifest if it is 0 bytes.
  3. Export a fresh manifest from the source instance before importing.
  4. Pre-validate with JsonUtils.fromJsonFile yourself and show a clearer error than 'malformed json'.

Example fix

// before: importing an empty file silently fails
Instances.installFromJson(repo, Path.of("empty.json"));
// after: guard first
String content = Files.readString(file);
if (content.isBlank()) throw new IllegalArgumentException("Manifest file is empty: " + file);
Instances.installFromJson(repo, file);
Defensive patterns

Strategy: validation

Validate before calling

// validate manifest file before calling installFromJson
static void requireInstallableManifest(Path file) throws IOException {
  if (!Files.isRegularFile(file) || Files.size(file) == 0)
    throw new IllegalArgumentException("manifest file missing or empty: " + file);
  GameInstanceManifest m = JsonUtils.fromJsonFile(file, GameInstanceManifest.class);
  if (m == null) throw new IllegalArgumentException("manifest is empty/null");
}

Try / catch

try {
  Instances.installFromJson(repository, file);
} catch (IllegalArgumentException e) {
  // installFromJson shows its own dialog and returns; only reached for pre-validation failures
  Controllers.dialog("Manifest file is empty or unreadable", i18n("message.error"),
      MessageDialogPane.MessageType.ERROR);
}

Prevention

When it happens

Trigger: Calling installFromJson(repository, path) with a file that is empty, contains only whitespace/null JSON, or cannot be deserialized into a non-null GameInstanceManifest.

Common situations: Selecting a zero-byte or placeholder file in the import-installer UI; a download of the manifest was interrupted leaving an empty file; pointing the installer at a non-manifest JSON file.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/ui/instances/Instances.java:179

        }, instanceId.toString(),
            new Validator(i18n("install.new_game.malformed"), HMCLGameRepository::isValidInstanceId),
            new Validator(i18n("install.new_game.already_exists"), newVersionName -> !repository.instanceIdConflicts(newVersionName) || newVersionName.equals(instanceId.toString())));
    }

    public static void exportInstance(HMCLGameInstance gameInstance) {
        Controllers.getDecorator().startWizard(new ExportWizardProvider(gameInstance), i18n("modpack.wizard"));
    }

    public static void openFolder(HMCLGameInstance gameInstance) {
        FXUtils.openFolder(gameInstance.getRunDirectory());
    }

    public static void installFromJson(HMCLGameRepository repository, Path file) {
        GameInstanceManifest manifest;
        try {
            manifest = JsonUtils.fromJsonFile(file, GameInstanceManifest.class);
            if (manifest == null)
                throw new IllegalArgumentException("Missing game manifest");
            if (manifest.inheritsFrom() != null)
                throw new IllegalArgumentException("Game manifest inherits from another manifest");
        } catch (Exception e) {
            LOG.warning("Failed to read game manifest from " + file, e);
            Controllers.dialog(i18n("install.new_game.malformed_json"), i18n("message.error"), MessageDialogPane.MessageType.ERROR);
            return;
        }

        Controllers.prompt(i18n("instance.manage.duplicate.prompt"), (result, handler) -> {
            handler.resolve();

            GameInstanceID instanceId = new GameInstanceID(result);

            DefaultDependencyManager dependencyManager = repository.getDependency();
            GameInstanceManifest newManifest = manifest.withId(instanceId).withJar(instanceId);
            GameDownloadTask gameDownloadTask = new GameDownloadTask(
                    dependencyManager,
                    newManifest);

View on GitHub (pinned to 24702dc5a0)