{"record":{"id":"d41bd790bd162da3","repo":"HMCL-dev/HMCL","slug":"duplicate-theme-pack-entry-entryname","errorCode":null,"errorMessage":"Duplicate theme-pack entry: ${entryName}","messagePattern":"Duplicate theme-pack entry: (.+?)","errorType":"validation","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java","lineNumber":1372,"sourceCode":"    }\n\n    /// Validates all zip entries in a theme-pack file.\n    private static void validateThemePackFile(Path themePackFile) throws IOException {\n        Set<String> entries = new HashSet<>();\n        boolean hasManifest = false;\n\n        try (ZipArchiveReader zipFile = new ZipArchiveReader(themePackFile, StandardCharsets.UTF_8)) {\n            for (ZipArchiveEntry entry : zipFile.getEntries()) {\n                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);","sourceCodeStart":1354,"sourceCodeEnd":1390,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java#L1354-L1390","documentation":"During theme-pack validation, ThemePackManager collects normalized entry names in a set; when a second entry shares an already-seen name, add() returns false and this IOException is thrown. Zip files technically allow duplicate entries, but duplicates make extraction ambiguous, so the archive is rejected.","triggerScenarios":"Installing a theme-pack zip that contains two entries with the same normalized name (e.g. 'assets/bg.png' twice, or 'assets/bg.png' plus 'assets\\bg.png' after normalization).","commonSituations":"Zips updated by appending a new version of a file without removing the old entry; archives concatenated from two packs; repacking scripts that add an entry twice.","solutions":["Inspect the zip for duplicates (`unzip -l pack.zip | sort | uniq -d`) and rebuild it without duplicates.","Re-export the theme pack using ThemePackExporter to guarantee a clean archive.","If producing zips programmatically, track entry names in a Set and skip/overwrite instead of writing duplicates."],"exampleFix":"// before\nfor (Path f : files) zip.addEntry(f.toString(), bytes); // may duplicate\n// after\nSet<String> seen = new HashSet<>();\nfor (Path f : files) {\n    String name = \"/\" + root.relativize(f);\n    if (seen.add(name)) zip.addEntry(name, bytes);\n}","handlingStrategy":"validation","validationCode":"Set<String> seen = new HashSet<>();\ntry (ZipFile z = new ZipFile(pack)) {\n    for (var e : Collections.list(z.entries())) {\n        if (!seen.add(e.getName().replace('\\\\', '/'))) throw new IllegalArgumentException(\"duplicate entry: \" + e.getName());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    ThemePackManager.install(pack, dir);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"Duplicate theme-pack entry\")) {\n        pack = rebuildWithoutDuplicates(pack);\n        ThemePackManager.install(pack, dir);\n    } else throw e;\n}","preventionTips":["Never append-update zips; rewrite them from scratch.","Track written entry names in a Set when generating archives.","Sanity-check repacked archives with unzip -l | uniq -d."],"tags":["zip","theme-pack","duplicate-entry"],"backgroundTag":"file-already-exists","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"}