HMCL-dev/HMCL · error · IOException

Installed theme-pack file is missing:

Error message

Installed theme-pack file is missing: 

What it means

When the theme-pack location is a zip file, resolveInstalledAsset first requires the zip itself to exist and be a regular file. If the pack file is gone or is not a regular file (e.g. a directory at that path), this IOException is thrown with the full path before any zip entry lookup happens.

Solutions

  1. Check Files.isRegularFile(packPath) before resolving and reinstall the pack if missing (ThemePackManager.install).
  2. Point the location at a pack inside the managed theme-packs directory instead of an ad-hoc path.
  3. Refresh stored theme-pack locations after any change to the launcher data directory.

Example fix

// before
resolveInstalledAsset(location, entry);
// after
if (location.file() != null && Files.isRegularFile(location.file())) {
    resolveInstalledAsset(location, entry);
}
Defensive patterns

Strategy: validation

Validate before calling

if (location.file() == null || !Files.isRegularFile(location.file().toAbsolutePath().normalize())) {
    // pack file missing; reinstall before resolving assets
    return;
}

Try / catch

try {
    ThemePackManager.resolveInstalledAsset(location, entry);
} catch (IOException e) {
    if (e.getMessage().startsWith("Installed theme-pack file is missing")) {
    // prompt the user to reinstall the pack
    }
}

Prevention

When it happens

Trigger: Calling resolveInstalledAsset() with a zip-backed ThemePackLocation whose file was deleted, moved, renamed, or replaced by a directory after installation.

Common situations: The user manually deleting the .zip from the theme-packs directory while settings still reference it; a synced/cleaned Downloads folder where the pack was never installed; portable installs where the packs directory moved.

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/7b277b50216b08b0. Report an issue: GitHub.

Appendix: source

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

        }

        @Nullable Path file = location.file();
        if (file == null) {
            throw new IOException("Theme pack location has no readable file: " + location);
        }
        Path installedFile = file.toAbsolutePath().normalize();
        if (Files.isDirectory(installedFile)) {
            Path assetFile = installedFile.resolve(normalizedEntryName).normalize();
            if (!assetFile.startsWith(installedFile)) {
                throw new IOException("Theme-pack asset escapes the installed directory: " + normalizedEntryName);
            }
            if (!Files.isRegularFile(assetFile)) {
                throw new IOException("Installed theme-pack asset is missing: " + normalizedEntryName);
            }
            return new ThemePackResource.File(assetFile, normalizedEntryName);
        }
        if (!Files.isRegularFile(installedFile)) {
            throw new IOException("Installed theme-pack file is missing: " + installedFile);
        }

        try (ZipArchiveReader zipFile = new ZipArchiveReader(installedFile, StandardCharsets.UTF_8)) {
            ZipArchiveEntry entry = zipFile.getEntry(normalizedEntryName);
            if (entry == null || entry.isDirectory()) {
                throw new IOException("Installed theme-pack asset is missing: " + normalizedEntryName);
            }
        }
        return new ThemePackResource.Zip(installedFile, normalizedEntryName);
    }

    /// Resolves one asset stored in a bundled theme pack.
    private static ThemePackResource resolveBuiltinAsset(ThemePackLocation.Builtin builtin, String entryName) throws IOException {
        String id = ThemePackManifest.requirePackageId(builtin.id());
        String resourcePath = "/assets/themes/" + id + "/" + entryName;
        try (InputStream input = ThemePackManager.class.getResourceAsStream(resourcePath)) {
            if (input != null) {
                return new ThemePackResource.Builtin(resourcePath, entryName);

View on GitHub (pinned to 24702dc5a0)