HMCL-dev/HMCL · error · IOException
Theme pack does not contain
Error message
Theme pack does not contain ${ThemePackExporter.MANIFEST_ENTRY} What it means
Theme packs must contain a manifest entry (ThemePackExporter.MANIFEST_ENTRY, e.g. 'theme-pack.json'). After scanning all zip entries, if none matched the manifest name, ThemePackManager throws this IOException. The manifest is required to identify and validate the pack's contents.
Solutions
- Add the required manifest file (theme-pack.json per ThemePackExporter.MANIFEST_ENTRY) at the archive root and repack.
- Re-export the pack with ThemePackExporter, which always writes the manifest.
- Verify you selected the correct file — this error often means the zip is not a theme pack at all.
Example fix
// before zip -r pack.zip assets/ // after zip -r pack.zip theme-pack.json assets/
Defensive patterns
Strategy: validation
Validate before calling
try (ZipFile z = new ZipFile(pack)) {
boolean has = Collections.list(z.entries()).stream().anyMatch(e -> e.getName().equals("theme-pack.json"));
if (!has) throw new IllegalArgumentException("not a theme pack: missing theme-pack.json");
} Try / catch
try {
ThemePackManager.install(pack, dir);
} catch (IOException e) {
if (e.getMessage().contains("does not contain")) {
ui.show("Selected file is not a theme pack (missing theme-pack.json)");
} else throw e;
} Prevention
- Check the file has a theme-pack.json at root before install.
- Always export via ThemePackExporter so the manifest is written.
- Don't rename the manifest file when editing packs.
When it happens
Trigger: Installing a zip that only contains an assets/ directory without the manifest file; renaming or deleting theme-pack.json before repacking; picking an unrelated zip as a theme pack.
Common situations: Hand-assembled packs missing the manifest; users renaming the manifest file; exporting assets without using ThemePackExporter; old-format theme files predating the manifest requirement.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- Duplicate theme-pack zip entry:
- Theme pack does not contain
- Installed theme-pack file is missing:
- Theme-pack entry name is not normalized
- Duplicate theme-pack entry
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/3020b9cc95836f38.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java:1381
String rawEntryName = entry.getName();
String entryName = normalizeThemePackEntryName(rawEntryName);
String canonicalEntryName = entry.isDirectory() ? entryName + "/" : entryName;
if (!canonicalEntryName.equals(rawEntryName)) {
throw new IOException("Theme-pack entry name is not normalized: " + rawEntryName);
}
checkSupportedThemePackEntry(entryName);
if (!entries.add(entryName)) {
throw new IOException("Duplicate theme-pack entry: " + entryName);
}
if (ThemePackExporter.MANIFEST_ENTRY.equals(entryName) && !entry.isDirectory()) {
hasManifest = true;
}
}
}
if (!hasManifest) {
throw new IOException("Theme pack does not contain " + ThemePackExporter.MANIFEST_ENTRY);
}
}
/// Moves a file into place, using an atomic move when the platform supports it.
private static void moveReplacing(Path source, Path target) throws IOException {
try {
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
} catch (AtomicMoveNotSupportedException e) {
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
}
}
/// Returns a normalized and safe theme-pack zip entry name.
private static String normalizeThemePackEntryName(String entryName) throws IOException {
Objects.requireNonNull(entryName);
String normalized = entryName.trim().replace('\\', '/');
if (normalized.endsWith("/")) {View on GitHub (pinned to 24702dc5a0)