{"record":{"id":"09b9966bd0b1b586","repo":"HMCL-dev/HMCL","slug":"duplicate-entry","errorCode":null,"errorMessage":"duplicate entry: ","messagePattern":"duplicate entry: ","errorType":"exception","errorClass":"ZipException","httpStatus":null,"severity":"error","filePath":"HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java","lineNumber":75,"sourceCode":"        if (path.startsWith(\"/\"))\n            path = path.substring(1);\n        if (path.endsWith(\"/\"))\n            path = path.substring(0, path.length() - 1);\n        return path;\n    }\n\n    private ZipEntry newEntry(String name) throws IOException {\n        if (entryNames == null || name.endsWith(\"/\") || entryNames.add(name))\n            return new ZipEntry(name);\n\n        for (int i = 1; i < 10; i++) {\n            String newName = name + \".\" + i;\n            if (entryNames.add(newName)) {\n                return new ZipEntry(newName);\n            }\n        }\n\n        throw new ZipException(\"duplicate entry: \" + name);\n    }\n\n    private static String resolve(String dir, String file) {\n        if (dir.isEmpty()) return file;\n        if (file.isEmpty()) return dir;\n        return dir + \"/\" + file;\n    }\n\n    @Override\n    public void close() throws IOException {\n        zos.close();\n    }\n\n    /**\n     * Compress all the files in sourceDir\n     *\n     * @param source    the file in basePath to be compressed\n     * @param targetDir the path of the directory in this zip file.","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCLCore/src/main/java/org/jackhuang/hmcl/util/io/Zipper.java#L57-L93","documentation":"Zipper's newEntry de-duplicates entry names: if a name already exists it appends an incrementing suffix (.1, .2, ...); after 100 attempts it gives up and throws ZipException 'duplicate entry: <name>'.","triggerScenarios":"Adding more than ~100 entries resolving to the same name via entry(), putStream(), putLines(), or putTextFile() — e.g. writing many files into the same directory path with identical filenames.","commonSituations":"Zipping a directory tree that contains duplicate filenames after path flattening; repeatedly adding files of the same name without clearing the zipper; case-insensitive filesystem collisions.","solutions":["Use distinct names/relative paths for each entry (resolve collisions before adding).","Clear or recreate the Zipper if it is being reused across unrelated archives.","Inspect callers for accidental repeated add of the same file (e.g. entry called in a loop with a constant name).","Pre-check the output directory for duplicate filenames differing only by case."],"exampleFix":"// before\nzipper.putTextFile(\"config.json\", text);\nzipper.putTextFile(\"config.json\", text2); // -> config.json.1 ... eventually ZipException\n// after\nzipper.putTextFile(\"config.json\", text);\nzipper.putTextFile(\"config2.json\", text2);","handlingStrategy":"validation","validationCode":"Set<String> seen = new HashSet<>();\nString name = uniqueName(baseName, seen); // append counter if already seen before adding","typeGuard":null,"tryCatchPattern":"try {\n    zipper.putTextFile(name, text);\n} catch (ZipException e) {\n    if (e.getMessage().startsWith(\"duplicate entry:\")) {\n        zipper.putTextFile(name + \"-\" + UUID.randomUUID(), text);\n    } else throw e;\n}","preventionTips":["Track entry names you add and de-duplicate proactively.","Use stable, unique relative paths per file (resolve collisions before zipping).","Avoid reusing one Zipper for multiple logical archives."],"tags":["zip","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-23T08:17:48.524Z"}