{"record":{"id":"76246e4572e9c899","repo":"HMCL-dev/HMCL","slug":"theme-pack-asset-entry-must-be-a-file","errorCode":null,"errorMessage":"Theme-pack asset entry must be a file: ","messagePattern":"Theme-pack asset entry must be a file: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java","lineNumber":71,"sourceCode":"    ///\n    /// @param entryName the entry name to validate\n    /// @return the normalized entry name\n    /// @throws IllegalArgumentException if the entry name is unsafe or outside `assets/`\n    static String normalizeEntryName(String entryName) {\n        Objects.requireNonNull(entryName);\n\n        String normalized = entryName.trim().replace('\\\\', '/');\n        if (normalized.isEmpty()) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry is empty\");\n        }\n        if (normalized.startsWith(\"/\") || normalized.matches(\"^[A-Za-z]:.*\")) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry must be relative: \" + entryName);\n        }\n        if (!normalized.startsWith(ASSETS_PREFIX)) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry must be under assets/: \" + entryName);\n        }\n        if (normalized.endsWith(\"/\")) {\n            throw new IllegalArgumentException(\"Theme-pack asset entry must be a file: \" + entryName);\n        }\n\n        for (String segment : normalized.split(\"/\")) {\n            if (segment.isEmpty() || \".\".equals(segment) || \"..\".equals(segment)) {\n                throw new IllegalArgumentException(\"Theme-pack asset entry contains an unsafe segment: \" + entryName);\n            }\n            for (int i = 0; i < segment.length(); i++) {\n                char ch = segment.charAt(i);\n                if (Character.isISOControl(ch) || ch == '\\0') {\n                    throw new IllegalArgumentException(\"Theme-pack asset entry contains a control character: \" + entryName);\n                }\n            }\n        }\n        return normalized;\n    }\n}\n","sourceCodeStart":53,"sourceCodeEnd":88,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java#L53-L88","documentation":"A theme-pack asset entry must point to a file, not a directory: a normalized name ending in '/' throws IllegalArgumentException(\"Theme-pack asset entry must be a file: <name>\"). Directory entries cannot identify an asset.","triggerScenarios":"Passing entry names like \"assets/icons/\" or \"assets/\" to ThemePackAsset / normalizeEntryName.","commonSituations":"Iterating all ZIP entries and accidentally feeding directory entries into the asset resolver, or a config value with a trailing slash.","solutions":["Remove the trailing slash and point to the concrete file, e.g. assets/icons/logo.png","Skip directory entries when iterating pack contents (check entry.isDirectory())","Fix the config value to a file path"],"exampleFix":"// before\nfor (entry : zip.entries()) use(entry.name()); // includes dirs\n// after\nfor (entry : zip.entries()) { if (!entry.isDirectory()) use(entry.name()); }","handlingStrategy":"validation","validationCode":"static boolean isFileEntry(ZipEntry e) {\n    return !e.isDirectory();\n}","typeGuard":"static boolean isFilePath(String n) {\n    return !n.endsWith(\"/\");\n}","tryCatchPattern":"try {\n    ThemePackAsset.of(entryName);\n} catch (IllegalArgumentException e) {\n    log.warn(\"Ignoring directory entry: \" + e.getMessage());\n}","preventionTips":["Skip isDirectory() entries when iterating ZIPs","Strip trailing slashes from config values","Point asset references at concrete files"],"tags":["validation","theme-pack","path"],"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"}