HMCL-dev/HMCL · error · IOException
Cannot delete a built-in theme pack:
Error message
Cannot delete a built-in theme pack:
What it means
ThemePackManager.uninstall(InstalledThemePack) refuses to remove packs flagged as built-in (bundled with the launcher resources). It throws this IOException carrying the pack's manifest id, because built-in packs have no deletable on-disk file managed by the launcher.
Solutions
- Check themePack.builtin() before calling uninstall and skip/exclude built-in packs from deletion.
- If the goal is to stop using a built-in pack, switch the selected theme to another pack instead of uninstalling it.
- Filter the pack list with p -> !p.builtin() wherever uninstall/delete actions are exposed.
Example fix
// before
for (InstalledThemePack p : packs) ThemePackManager.uninstall(p);
// after
for (InstalledThemePack p : packs) {
if (!p.builtin()) ThemePackManager.uninstall(p);
} Defensive patterns
Strategy: type-guard
Validate before calling
if (pack.builtin()) return; // nothing to delete; switch theme instead
Type guard
static boolean uninstallable(InstalledThemePack p) {
return !p.builtin() && p.file() != null;
} Try / catch
try {
ThemePackManager.uninstall(pack);
} catch (IOException e) {
if (e.getMessage().startsWith("Cannot delete a built-in theme pack")) {
// built-in packs are immutable; ignore or inform the user
}
} Prevention
- Filter pack lists with !builtin() before offering delete actions
- Treat built-in packs as read-only vocabulary, not user data
- To stop using a built-in pack, switch the selected theme instead
When it happens
Trigger: Calling ThemePackManager.uninstall() with an InstalledThemePack whose builtin() flag is true, i.e. any pack returned by loadBuiltinThemePacks() rather than installed into THEME_PACKS_DIRECTORY/USER_THEME_PACKS_DIRECTORY.
Common situations: UI code iterating all installed packs (including built-ins) and offering a delete action without filtering; scripted cleanup that deletes every pack in the list.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Theme pack does not have a local file:
- Theme-pack file is outside the managed directory:
- Theme-pack asset entry is empty
- Theme-pack asset entry must be relative:
- Theme-pack asset entry must be under assets/:
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/3a15e1c62a2385c4.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java:538
deleteIfExists(temporaryFile);
} catch (IOException suppressed) {
e.addSuppressed(suppressed);
}
throw e;
}
}
/// Removes an installed theme pack from a managed theme-pack directory.
///
/// If the removed package is currently selected, the stored theme reference is cleared.
///
/// @param themePack the installed theme pack to remove
/// @throws IOException if the installed package cannot be removed
public static void uninstall(InstalledThemePack themePack) throws IOException {
Objects.requireNonNull(themePack);
if (themePack.builtin()) {
throw new IOException("Cannot delete a built-in theme pack: " + themePack.manifest().id());
}
@Nullable Path file = themePack.file();
if (file == null) {
throw new IOException("Theme pack does not have a local file: " + themePack.manifest().id());
}
Path targetFile = file.toAbsolutePath().normalize();
Path localDirectory = THEME_PACKS_DIRECTORY.toAbsolutePath().normalize();
Path userDirectory = USER_THEME_PACKS_DIRECTORY.toAbsolutePath().normalize();
boolean localThemePack = targetFile.startsWith(localDirectory) && !targetFile.equals(localDirectory);
boolean userThemePack = targetFile.startsWith(userDirectory) && !targetFile.equals(userDirectory);
if (!localThemePack && !userThemePack) {
throw new IOException("Theme-pack file is outside the managed directory: " + targetFile);
}
deleteIfExists(targetFile);
View on GitHub (pinned to 24702dc5a0)