{"record":{"id":"27788d673bceec19","repo":"HMCL-dev/HMCL","slug":"theme-pack-file-is-outside-the-managed-directory","errorCode":null,"errorMessage":"Theme-pack file is outside the managed directory: ","messagePattern":"Theme-pack file is outside the managed directory: ","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java","lineNumber":552,"sourceCode":"    public static void uninstall(InstalledThemePack themePack) throws IOException {\n        Objects.requireNonNull(themePack);\n\n        if (themePack.builtin()) {\n            throw new IOException(\"Cannot delete a built-in theme pack: \" + themePack.manifest().id());\n        }\n\n        @Nullable Path file = themePack.file();\n        if (file == null) {\n            throw new IOException(\"Theme pack does not have a local file: \" + themePack.manifest().id());\n        }\n\n        Path targetFile = file.toAbsolutePath().normalize();\n        Path localDirectory = THEME_PACKS_DIRECTORY.toAbsolutePath().normalize();\n        Path userDirectory = USER_THEME_PACKS_DIRECTORY.toAbsolutePath().normalize();\n        boolean localThemePack = targetFile.startsWith(localDirectory) && !targetFile.equals(localDirectory);\n        boolean userThemePack = targetFile.startsWith(userDirectory) && !targetFile.equals(userDirectory);\n        if (!localThemePack && !userThemePack) {\n            throw new IOException(\"Theme-pack file is outside the managed directory: \" + targetFile);\n        }\n\n        deleteIfExists(targetFile);\n\n        ThemeReference reference = settings().getSelectedThemeOrDefault();\n        ThemePackManifest manifest = themePack.manifest();\n        if (reference.packId().equals(manifest.id())) {\n            @Nullable InstalledThemePack replacementThemePack = findInstalled(reference);\n            @Nullable Theme replacementTheme = replacementThemePack == null\n                    ? null\n                    : replacementThemePack.manifest().findTheme(reference.themeId());\n            if (replacementTheme == null) {\n                settings().selectedThemeProperty().set(BUILTIN_DEFAULT_THEME_REFERENCE);\n            }\n        }\n    }\n\n    /// Applies one theme from a loaded theme pack to current launcher settings.","sourceCodeStart":534,"sourceCodeEnd":570,"githubUrl":"https://github.com/HMCL-dev/HMCL/blob/24702dc5a0214034f4c27166d5fd30cad08cec19/HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java#L534-L570","documentation":"uninstall() validates that the pack's absolute normalized file lives under THEME_PACKS_DIRECTORY or USER_THEME_PACKS_DIRECTORY. If it resides anywhere else, the manager refuses to delete it and throws this IOException with the offending path, as a safety measure against removing arbitrary user files.","triggerScenarios":"Calling uninstall() on an InstalledThemePack whose file path points outside both managed theme-pack directories — e.g. a pack in the user's home or Downloads folder that was loaded via load() without ever being installed.","commonSituations":"Double-clicking a theme pack downloaded from the web and attempting to uninstall it directly; loading packs from a custom directory and then calling uninstall on them; symlinked or relocated install directories making the normalized path differ from expectations.","solutions":["Install the pack first with ThemePackManager.install(Path) so it is copied into a managed directory, then uninstall that installed copy.","If deleting an unmanaged pack, delete the file yourself (Files.deleteIfExists) instead of going through uninstall().","Check that THEME_PACKS_DIRECTORY/USER_THEME_PACKS_DIRECTORY are where you expect (they may change between HMCL versions or portable-mode configs)."],"exampleFix":"// before\nThemePackManager.uninstall(ThemePackManager.loadInstalled(downloadedZip));\n// after\nInstalledThemePack installed = ThemePackManager.install(downloadedZip);\nThemePackManager.uninstall(installed);","handlingStrategy":"validation","validationCode":"Path target = pack.file().toAbsolutePath().normalize();\nPath local = ThemePackManager.THEME_PACKS_DIRECTORY.toAbsolutePath().normalize();\nPath user = ThemePackManager.USER_THEME_PACKS_DIRECTORY.toAbsolutePath().normalize();\nboolean managed = (target.startsWith(local) && !target.equals(local))\n               || (target.startsWith(user) && !target.equals(user));\nif (!managed) {\n    // install the pack into a managed directory first, then uninstall that copy\n    InstalledThemePack inst = ThemePackManager.install(pack.file());\n}","typeGuard":null,"tryCatchPattern":"try {\n    ThemePackManager.uninstall(pack);\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Theme-pack file is outside the managed directory\")) {\n    // fall back to deleting the file directly or install-then-uninstall\n    }\n}","preventionTips":["Always install packs into the managed directories before managing their lifecycle","Load packs from ad-hoc paths read-only; never call uninstall on them","Check where THEME_PACKS_DIRECTORY points (portable mode can relocate it)"],"tags":["theme-pack","uninstall","path","safety-check"],"backgroundTag":"path-traversal-blocked","analyzedSha":"24702dc5a0214034f4c27166d5fd30cad08cec19","analyzedAt":"2026-09-10T12:36:46.680Z","contentChangedAt":"2026-09-10T12:36:46.680Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}