{"record":{"id":"4afabec5e05c5a45","repo":"HMCL-dev/HMCL","slug":"duplicate-theme-pack-zip-entry","errorCode":null,"errorMessage":"Duplicate theme-pack zip entry: ","messagePattern":"Duplicate theme-pack zip entry: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackExporter.java","lineNumber":111,"sourceCode":"        }\n    }\n\n    /// Validates all asset entries and source files before the zip is written.\n    private static void validateAssets(List<ThemePackAsset> assets) throws IOException {\n        Set<String> entries = new HashSet<>();\n        entries.add(MANIFEST_ENTRY);\n\n        for (ThemePackAsset asset : assets) {\n            Objects.requireNonNull(asset);\n            @Nullable Path sourceFile = asset.source().file();\n            if (sourceFile != null && !Files.isRegularFile(sourceFile)) {\n                throw new IOException(\"Theme-pack asset source is not a regular file: \" + sourceFile);\n            }\n            try (InputStream ignored = asset.source().openStream()) {\n                // Validate readability before writing the target zip.\n            }\n            if (!entries.add(asset.entryName())) {\n                throw new IllegalArgumentException(\"Duplicate theme-pack zip entry: \" + asset.entryName());\n            }\n        }\n    }\n}\n","sourceCodeStart":93,"sourceCodeEnd":116,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackExporter.java#L93-L116","documentation":"Thrown by ThemePackExporter.validateAssets when two or more assets map to the same zip entry name. A zip archive cannot contain duplicate entries reliably, so the exporter detects collisions in its entry-name set before writing the target file.","triggerScenarios":"Calling ThemePackExporter.export with an asset list where two ThemePackAsset items return the same entryName() — e.g. adding background.png twice, or two files in different source dirs assigned the same entry name.","commonSituations":"Programmatic asset assembly that appends defaults and user overrides without deduplication, copy-pasted asset registrations, or generating entry names that ignore case/extension differences that still normalize identically.","solutions":["Deduplicate the asset list by entryName() before calling export.","Use a Map keyed by entry name when building assets so later entries replace earlier ones.","Give colliding assets distinct entry names if both are truly needed.","Log or merge duplicate registrations at asset-collection time instead of passing them through."],"exampleFix":"// before\nassets.add(ThemePackAsset.of(\"background.png\", a));\nassets.add(ThemePackAsset.of(\"background.png\", b));\n// after\nassets.add(ThemePackAsset.of(\"background.png\", b)); // keep only one entry per name","handlingStrategy":"validation","validationCode":"java.util.Set<String> seen = new java.util.HashSet<>();\nfor (ThemePackAsset a : assets) {\n    if (!seen.add(a.entryName())) throw new IllegalStateException(\"Duplicate entry: \" + a.entryName());\n}","typeGuard":null,"tryCatchPattern":"try { ThemePackExporter.export(pack, assets, out); } catch (IllegalArgumentException e) { if (e.getMessage().startsWith(\"Duplicate theme-pack zip entry\")) dedupeAssets(); throw e; }","preventionTips":["Build assets in a Map keyed by entry name so duplicates overwrite","Deduplicate by entryName() before export","Use stable, unique entry-name generation for programmatic packs"],"tags":["theme-pack","export","zip","duplicate-key"],"backgroundTag":"invalid-argument-value","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}