HMCL-dev/HMCL · error · IOException

Theme pack location has no readable file:

Error message

Theme pack location has no readable file: 

What it means

resolveInstalledAsset(location, entryName) needs a concrete file backing the theme-pack location. If the ThemePackLocation is not Builtin and its file() returns null, there is no readable source for assets and this IOException is thrown with the location's string form.

Solutions

  1. Ensure the location refers to an installed pack directory or zip file (set file() via ThemePackManager.install or a valid ThemePackLocation.File).
  2. Check location.file() != null before resolving assets and surface a user-facing 'install the pack first' message otherwise.
  3. Re-resolve the location from the pack manager (loadInstalled) so it carries a real path.

Example fix

// before
ThemePackManager.resolveInstalledAsset(location, entryName);
// after
if (location instanceof ThemePackLocation.File f && f.file() != null) {
    ThemePackManager.resolveInstalledAsset(location, entryName);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!(location instanceof ThemePackLocation.Builtin) && location.file() == null) {
    // surface 'install the pack first' instead of resolving
    return;
}

Type guard

static boolean resolvable(ThemePackLocation loc) {
    return loc instanceof ThemePackLocation.Builtin
        || (loc.file() != null && (Files.isDirectory(loc.file()) || Files.isRegularFile(loc.file())));
}

Try / catch

try {
    ThemePackManager.resolveInstalledAsset(location, entry);
} catch (IOException e) {
    if (e.getMessage().startsWith("Theme pack location has no readable file")) {
    // reinstall/re-resolve the location before retrying
    }
}

Prevention

When it happens

Trigger: Calling ThemePackManager.resolveInstalledAsset() with a non-builtin ThemePackLocation whose file is null — e.g. a location descriptor for a pack that was never installed or whose path was cleared.

Common situations: Resolving assets for a pack record persisted to config without its path; constructing ThemePackLocation instances manually with null files; resolving assets after the pack registry lost its mapping.

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

Appendix: source

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

                opacity);
    }

    /// Resolves one asset referenced by an installed theme pack.
    ///
    /// @param location the installed theme-pack location
    /// @param entryName the theme-pack relative asset entry name
    /// @return the resolved resource
    /// @throws IOException if the asset cannot be read
    public static ThemePackResource resolveInstalledAsset(ThemePackLocation location, String entryName) throws IOException {
        Objects.requireNonNull(location);
        String normalizedEntryName = ThemePackAsset.normalizeEntryName(entryName);
        if (location instanceof ThemePackLocation.Builtin builtin) {
            return resolveBuiltinAsset(builtin, normalizedEntryName);
        }

        @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);

View on GitHub (pinned to 24702dc5a0)