{"record":{"id":"3020b9cc95836f38","repo":"HMCL-dev/HMCL","slug":"theme-pack-does-not-contain-themepackexporter-ma","errorCode":null,"errorMessage":"Theme pack does not contain ${ThemePackExporter.MANIFEST_ENTRY}","messagePattern":"Theme pack does not contain (.+?)","errorType":"validation","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java","lineNumber":1381,"sourceCode":"                String rawEntryName = entry.getName();\n                String entryName = normalizeThemePackEntryName(rawEntryName);\n                String canonicalEntryName = entry.isDirectory() ? entryName + \"/\" : entryName;\n                if (!canonicalEntryName.equals(rawEntryName)) {\n                    throw new IOException(\"Theme-pack entry name is not normalized: \" + rawEntryName);\n                }\n                checkSupportedThemePackEntry(entryName);\n\n                if (!entries.add(entryName)) {\n                    throw new IOException(\"Duplicate theme-pack entry: \" + entryName);\n                }\n                if (ThemePackExporter.MANIFEST_ENTRY.equals(entryName) && !entry.isDirectory()) {\n                    hasManifest = true;\n                }\n            }\n        }\n\n        if (!hasManifest) {\n            throw new IOException(\"Theme pack does not contain \" + ThemePackExporter.MANIFEST_ENTRY);\n        }\n    }\n\n    /// Moves a file into place, using an atomic move when the platform supports it.\n    private static void moveReplacing(Path source, Path target) throws IOException {\n        try {\n            Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);\n        } catch (AtomicMoveNotSupportedException e) {\n            Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);\n        }\n    }\n\n    /// Returns a normalized and safe theme-pack zip entry name.\n    private static String normalizeThemePackEntryName(String entryName) throws IOException {\n        Objects.requireNonNull(entryName);\n\n        String normalized = entryName.trim().replace('\\\\', '/');\n        if (normalized.endsWith(\"/\")) {","sourceCodeStart":1363,"sourceCodeEnd":1399,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java#L1363-L1399","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-assembled packs missing the manifest; users renaming the manifest file; exporting assets without using ThemePackExporter; old-format theme files predating the manifest requirement.","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."],"exampleFix":"// before\nzip -r pack.zip assets/\n// after\nzip -r pack.zip theme-pack.json assets/","handlingStrategy":"validation","validationCode":"try (ZipFile z = new ZipFile(pack)) {\n    boolean has = Collections.list(z.entries()).stream().anyMatch(e -> e.getName().equals(\"theme-pack.json\"));\n    if (!has) throw new IllegalArgumentException(\"not a theme pack: missing theme-pack.json\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    ThemePackManager.install(pack, dir);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"does not contain\")) {\n      ui.show(\"Selected file is not a theme pack (missing theme-pack.json)\");\n    } else throw e;\n}","preventionTips":["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."],"tags":["zip","theme-pack","missing-manifest"],"backgroundTag":"missing-required-config-field","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}