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
- Check Files.isRegularFile(packPath) before resolving and reinstall the pack if missing (ThemePackManager.install).
- Point the location at a pack inside the managed theme-packs directory instead of an ad-hoc path.
- 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
- Install packs into the managed directory instead of referencing downloads
- Detect missing pack files at startup and prune stale locations
- Avoid moving or renaming pack zips while the launcher references them
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
- Installed theme-pack asset is missing:
- Duplicate theme-pack zip entry:
- Theme pack does not contain
- Theme pack location has no readable file:
- Theme-pack asset escapes the installed directory:
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)