HMCL-dev/HMCL · error · IOException
Cannot export a background directory as a theme-pack asset
Error message
Cannot export a background directory as a theme-pack asset: ${source} What it means
Thrown by ThemePackManager when exporting a theme pack whose background resolves to a directory rather than an image file. Theme packs bundle backgrounds as ZIP entries under assets/wallpapers/, and a directory cannot be stored as a single asset. The manager fails fast before building the pack.
Solutions
- Change the theme's background image path to point at the actual image file inside the directory (e.g. dir/wallpaper.png instead of dir).
- If the directory contains the intended image, list its contents and pick a regular file; verify with Files.isRegularFile before exporting.
- Fix any symlink or path-join mistake that makes imagePath resolve to a directory instead of a file.
Example fix
// before
"background": { "imagePath": "/home/user/wallpapers/" }
// after
"background": { "imagePath": "/home/user/wallpapers/sunset.png" } Defensive patterns
Strategy: validation
Validate before calling
Path p = Path.of(configuredPath).toAbsolutePath().normalize();
if (Files.isDirectory(p)) throw new IllegalArgumentException("background must be an image file, not a directory: " + p);
if (!Files.isRegularFile(p)) throw new IllegalArgumentException("background image missing: " + p); Prevention
- Point background imagePath at files, never directories, when authoring theme manifests.
- Run Files.isRegularFile checks on all asset paths before export.
When it happens
Trigger: Calling the theme-pack export flow with a background whose imagePath() points at a directory (Files.isDirectory(source) is true after toAbsolutePath().normalize()).
Common situations: A theme JSON/manifest configured with a background path that accidentally points at a folder (e.g. an extracted wallpaper directory instead of the wallpaper file inside it); a misconfigured custom-background setting where the user selected a directory; a symlink resolving to a directory.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Theme background image does not exist
- Theme-pack asset entry is empty
- Theme-pack asset entry must be relative:
- Theme-pack asset entry must be under assets/:
- Theme-pack asset entry must be a file:
AI-assisted analysis of HMCL-dev/HMCL@24702dc5a0 (2026-09-10).
Data as JSON: /api/errors/2ec60db487880cfe.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java:1483
}
/// Creates the image background source for the resolved current background image.
private static ThemeBackground.Image createCurrentImageBackgroundSource(
List<ThemePackAsset> assets,
ResolvedBackground background) throws IOException {
@Nullable ThemePackResource imageResource = background.imageResource();
if (imageResource != null) {
assets.add(new ThemePackAsset(imageResource, imageResource.name()));
return new ThemeBackground.Image(imageResource.name());
}
@Nullable Path imagePath = background.imagePath();
if (imagePath == null) {
throw new IOException("Theme background image path is not configured");
}
Path source = imagePath.toAbsolutePath().normalize();
if (Files.isDirectory(source)) {
throw new IOException("Cannot export a background directory as a theme-pack asset: " + source);
}
if (!Files.isRegularFile(source)) {
throw new IOException("Theme background image does not exist: " + source);
}
String entryName = "assets/wallpapers/" + sanitizePathSegment(source.getFileName().toString());
assets.add(new ThemePackAsset(source, entryName));
return new ThemeBackground.Image(entryName);
}
/// Downloads the current network background and exports it as a theme-pack image asset.
private static ThemeBackground.Image createCurrentNetworkBackgroundSource(
List<ThemePackAsset> assets,
List<Path> temporaryFiles,
ResolvedBackground background) throws IOException {
String url = requireNonBlank(background.networkImageUrl(), "background.url");
URI uri = NetworkUtils.toURI(url);
if (!NetworkUtils.isHttpUri(uri)) {View on GitHub (pinned to 24702dc5a0)