{"record":{"id":"9f66279d4a7ccf62","repo":"HMCL-dev/HMCL","slug":"theme-pack-entry-name-is-not-normalized-rawentr","errorCode":null,"errorMessage":"Theme-pack entry name is not normalized: ${rawEntryName}","messagePattern":"Theme-pack entry name is not normalized: (.+?)","errorType":"validation","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java","lineNumber":1367,"sourceCode":"            if (themePack.manifest().id().equals(packId)) {\n                return themePack;\n            }\n        }\n        return null;\n    }\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.","sourceCodeStart":1349,"sourceCodeEnd":1385,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java#L1349-L1385","documentation":"When installing a theme pack, ThemePackManager iterates zip entries and normalizes each name (backslashes to forward slashes, trailing slash stripped). If the normalized name differs from the raw zip entry name, the archive is rejected with this IOException. This enforces a strict, portable entry-name layout so extraction paths are deterministic.","triggerScenarios":"Opening a theme-pack zip whose entries were created with Windows-style backslash separators, redundant trailing slashes on file entries, or non-canonical names produced by a third-party zip tool.","commonSituations":"Repacking a theme pack on Windows with a tool that writes \\ separators; zips produced by scripts appending '/' inconsistently; archives edited/rewritten by older library versions with different normalization rules.","solutions":["Recreate the zip so every entry uses forward-slash paths and no redundant trailing slashes (e.g. `zip -r pack.zip assets theme-pack.json` from a Unix tool).","Re-export the theme pack with the library's own ThemePackExporter instead of repacking manually.","Normalize entry names in the archive with a zip-repair tool before installing.","If you control the producing code, write ZipArchiveEntry names via Path.getName() joined with '/' rather than raw OS paths."],"exampleFix":"// before\nzip.putNextEntry(new ZipArchiveEntry(\"assets\\\\background.png\"));\n// after\nzip.putNextEntry(new ZipArchiveEntry(\"assets/background.png\"));","handlingStrategy":"validation","validationCode":"try (ZipFile z = new ZipFile(pack)) {\n    for (var e : Collections.list(z.entries())) {\n        String n = e.getName().replace('\\\\', '/');\n        if (!e.getName().equals(n) || n.endsWith(\"/\")) throw new IllegalArgumentException(\"non-normalized entry: \" + e.getName());\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    ThemePackManager.install(pack, dir);\n} catch (IOException e) {\n    if (e.getMessage().contains(\"not normalized\")) {\n        repackNormalized(pack);\n        ThemePackManager.install(pack, dir);\n    } else throw e;\n}","preventionTips":["Always create zips with forward-slash entry names.","Prefer ThemePackExporter over third-party packers.","Pre-validate user-supplied packs before install."],"tags":["zip","theme-pack","validation"],"backgroundTag":"invalid-argument-format","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"}