{"record":{"id":"6a82ee2a1b477cb7","repo":"HMCL-dev/HMCL","slug":"theme-pack-asset-entry-is-empty","errorCode":null,"errorMessage":"Theme-pack asset entry is empty","messagePattern":"Theme-pack asset entry is empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java","lineNumber":62,"sourceCode":"    ///\n    /// @param source    the resource to copy\n    /// @param entryName the normalized zip entry name under `assets/`\n    public ThemePackAsset {\n        Objects.requireNonNull(source);\n        entryName = normalizeEntryName(entryName);\n    }\n\n    /// Normalizes and validates a zip entry name.\n    ///\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') {","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java#L44-L80","documentation":"normalizeEntryName validates theme-pack ZIP entry names before use. After trimming and normalizing backslashes to slashes, an empty name throws IllegalArgumentException(\"Theme-pack asset entry is empty\"). The name is used for path-safe asset lookup, so emptiness is rejected first.","triggerScenarios":"Passing \"\" or a whitespace-only string (e.g. \"   \") to ThemePackAsset construction / normalizeEntryName.","commonSituations":"Reading a ZIP manifest or config where an entry key was left blank, or programmatically building asset names with an unset variable.","solutions":["Provide a non-empty asset entry name starting with assets/","Check the source data (ZIP manifest, config) for blank entry names before constructing ThemePackAsset","Ensure the variable supplying the entry name is actually initialized"],"exampleFix":"// before\nString name = cfg.get(\"icon\"); // \"\"\nThemePackAsset.of(name);\n// after\nString name = cfg.get(\"icon\");\nif (name != null && !name.isBlank()) ThemePackAsset.of(name);","handlingStrategy":"validation","validationCode":"static boolean isUsableEntryName(String name) {\n    return name != null && !name.trim().isEmpty();\n}","typeGuard":"static boolean isNonEmpty(String s) {\n    return s != null && !s.isBlank();\n}","tryCatchPattern":"try {\n    ThemePackAsset.of(entryName);\n} catch (IllegalArgumentException e) {\n    log.warn(\"Ignoring bad asset entry: \" + e.getMessage());\n}","preventionTips":["Never pass null/blank entry names; check before constructing assets","Validate pack manifests for blank entry keys","Initialize the variable supplying the entry name"],"tags":["validation","theme-pack","arguments"],"backgroundTag":"empty-required-field","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"}