{"record":{"id":"d041910771b1e0dc","repo":"HMCL-dev/HMCL","slug":"theme-pack-asset-entry-must-be-relative","errorCode":null,"errorMessage":"Theme-pack asset entry must be relative: ","messagePattern":"Theme-pack asset entry must be relative: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java","lineNumber":65,"sourceCode":"    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') {\n                    throw new IllegalArgumentException(\"Theme-pack asset entry contains a control character: \" + entryName);\n                }\n            }","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackAsset.java#L47-L83","documentation":"normalizeEntryName rejects entry names that are absolute paths: names starting with '/' or matching a Windows drive prefix like 'C:' throw IllegalArgumentException(\"Theme-pack asset entry must be relative: <name>\"). Theme-pack assets are looked up inside the pack under assets/, so absolute paths are never valid.","triggerScenarios":"Passing entries like \"/assets/icon.png\", \"C:\\\\assets\\\\icon.png\", or \"D:/x.png\" to ThemePackAsset / normalizeEntryName.","commonSituations":"Theme pack built on Windows with absolute file paths in its manifest, or code concatenating a filesystem path instead of a pack-relative entry name.","solutions":["Strip the leading '/' or drive prefix and use a pack-relative name beginning with assets/","Use Paths/zip-entry relative names when building the pack instead of absolute file paths","Normalize with the pack root before passing the name"],"exampleFix":"// before\nThemePackAsset.of(\"/assets/icon.png\");\n// after\nThemePackAsset.of(\"assets/icon.png\");","handlingStrategy":"validation","validationCode":"static String toRelativeEntry(String name) {\n    String n = name.replace('\\\\', '/');\n    if (n.matches(\"^[A-Za-z]:.*\")) n = n.substring(2);\n    while (n.startsWith(\"/\")) n = n.substring(1);\n    return n;\n}","typeGuard":"static boolean isRelative(String n) {\n    String s = n.replace('\\\\', '/');\n    return !s.startsWith(\"/\") && !s.matches(\"^[A-Za-z]:.*\");\n}","tryCatchPattern":"try {\n    ThemePackAsset.of(entryName);\n} catch (IllegalArgumentException e) {\n    log.warn(\"Ignoring non-relative asset entry: \" + e.getMessage());\n}","preventionTips":["Store pack-relative names (assets/...) in manifests, never absolute paths","When building packs on Windows, strip drive letters and leading slashes","Use ZIP entry names rather than filesystem paths"],"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"}