HMCL-dev/HMCL · error · IOException

Invalid theme-pack manifest

Error message

Invalid theme-pack manifest

What it means

ThemePackManager.load() throws this IOException when the theme-pack manifest (MANIFEST_ENTRY inside the pack file or directory) fails to parse: JsonUtils.fromNonNullJsonFully/fromJsonFile raises JsonParseException, or LoadedThemePack's compact constructor raises IllegalArgumentException. The original exception is chained as the cause. It signals a corrupt, malformed, or incomplete theme pack rather than an I/O failure.

Solutions

  1. Open the cause (e.getCause()) to see the exact JsonParseException/IllegalArgumentException message and fix the manifest JSON accordingly.
  2. Re-export or re-download the theme pack from a trusted source to replace the corrupt file.
  3. Validate the manifest JSON manually (e.g. with a JSON linter) against ThemePackManifest's required fields before distributing the pack.
  4. If the pack was produced by a different HMCL version, regenerate it with ThemePackExporter using a compatible schema.

Example fix

// before: blindly loading an untrusted pack
LoadedThemePack pack = ThemePackManager.load(file);
// after: verify the manifest parses before use
try {
    LoadedThemePack pack = ThemePackManager.load(file);
} catch (IOException e) {
    log.warning("Discarding corrupt theme pack " + file + ": " + e.getCause());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before load
if (Files.isRegularFile(file) && !Files.isExecutable(file)) throw new IllegalStateException("not readable");
try (var zis = new java.util.zip.ZipFile(file.toFile())) {
    if (zis.getEntry("theme-pack.json") == null) throw new IllegalStateException("pack missing manifest entry");
}
// for a directory pack:
if (Files.isDirectory(file) && !Files.isRegularFile(file.resolve("theme-pack.json")))
    throw new IllegalStateException("pack directory missing theme-pack.json");

Type guard

static boolean looksLikeThemePack(Path p) throws IOException {
    if (Files.isDirectory(p)) return Files.isRegularFile(p.resolve("theme-pack.json"));
    if (!Files.isRegularFile(p)) return false;
    try (var zis = new java.util.zip.ZipFile(p.toFile())) {
        return zis.getEntry("theme-pack.json") != null;
    }
}

Try / catch

try {
    LoadedThemePack pack = ThemePackManager.load(file);
} catch (IOException e) {
    if (e.getMessage().startsWith("Invalid theme-pack manifest")) {
    logger.warn("Corrupt theme pack " + file + ": " + e.getCause());
    }
}

Prevention

When it happens

Trigger: Calling ThemePackManager.load(Path) (or loadInstalled/install, which delegate to it) on a pack whose theme-pack manifest JSON is syntactically invalid, missing required fields, violates ThemePackManifest record constraints, or whose manifest stream deserializes to null.

Common situations: Hand-edited manifest.json with a JSON typo; a pack exported by a newer HMCL version with an incompatible schema; a truncated or partially downloaded .zip; a renamed/mangled file given a .zip extension; a zip whose manifest entry is not UTF-8 JSON.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java:398

                    throw new JsonParseException("Manifest is null");
                }
                return new LoadedThemePack(normalizedFile, manifest);
            }

            try (var reader = new ZipArchiveReader(normalizedFile)) {
                var manifestEntry = reader.getEntry(ThemePackExporter.MANIFEST_ENTRY);
                if (manifestEntry == null || manifestEntry.isDirectory()) {
                    throw new IOException("Theme pack does not contain " + ThemePackExporter.MANIFEST_ENTRY);
                }

                ThemePackManifest manifest;
                try (var inputStream = reader.getInputStream(manifestEntry)) {
                    manifest = JsonUtils.fromNonNullJsonFully(inputStream, ThemePackManifest.class);
                }
                return new LoadedThemePack(normalizedFile, manifest);
            }
        } catch (JsonParseException | IllegalArgumentException e) {
            throw new IOException("Invalid theme-pack manifest", e);
        }
    }

    /// Loads built-in theme pack manifests from launcher resources.
    private static @Unmodifiable List<InstalledThemePack> loadBuiltinThemePacks() {
        ArrayList<InstalledThemePack> themePacks = new ArrayList<>();
        for (String id : BUILTIN_THEME_PACK_IDS) {
            try {
                ThemePackManifest manifest;

                try (InputStream input = ThemePackManager.class.getResourceAsStream(
                        "/assets/themes/" + id + "/" + ThemePackExporter.MANIFEST_ENTRY)) {
                    if (input == null) {
                        throw new IOException("Missing built-in theme-pack manifest: " + id);
                    }

                    manifest = JsonUtils.fromNonNullJsonFully(input, ThemePackManifest.class);
                }

View on GitHub (pinned to 24702dc5a0)