HMCL-dev/HMCL · error · IOException
Built-in theme-pack asset is missing
Error message
Built-in theme-pack asset is missing: ${entryName} What it means
ThemePackManager resolves built-in theme-pack assets from the classpath at /assets/themes/<id>/<entryName>. This IOException is thrown when that classpath resource does not exist, meaning the packaged jar is missing the expected bundled asset. It signals a build/packaging defect rather than a user problem, since built-in assets ship with the library.
Solutions
- Verify /assets/themes/<id>/<entryName> exists in the jar on the classpath (unzip -l hmcl.jar | grep assets/themes).
- Rebuild the project so theme resources are copied into the jar; check build config resource excludes.
- Confirm the theme id and entryName are valid for this library version (names may have changed between versions).
- If using a custom classloader, ensure it can load resources from the HMCL jar.
Example fix
// before
ThemePackResource res = ThemePackManager.resolveBuiltinAsset("legacy", "theme.css");
// after
if (ThemePackManager.builtinAssetExists("modern", "theme.css")) {
ThemePackResource res = ThemePackManager.resolveBuiltinAsset("modern", "theme.css");
} else {
ThemePackResource res = ThemePackManager.resolveDefaultAsset();
} Defensive patterns
Strategy: fallback
Validate before calling
boolean ok = ThemePackManager.class.getResourceAsStream("/assets/themes/" + id + "/" + entryName) != null;
if (!ok) useDefaultThemePack(); Try / catch
try {
resource = ThemePackManager.resolveBuiltinAsset(id, entryName);
} catch (IOException e) {
LOG.warn("builtin theme asset missing: {}", e.getMessage());
resource = ThemePackManager.resolveDefaultAsset();
} Prevention
- Check build resource filters after any build-config change.
- List the jar contents in CI to assert required theme assets exist.
- Avoid hardcoding theme ids/entries; enumerate them from a registry.
When it happens
Trigger: Calling the public built-in asset resolver (ThemePackManager, public API) with a theme-pack id + entryName whose path /assets/themes/<id>/<entryName> has no classpath resource; input from getResourceAsStream is null.
Common situations: Running against a partially built or shaded jar where assets were excluded by build filters; referencing a theme id or entry that was removed/renamed in a newer version; custom classloaders that do not expose the jar's resources.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Built-in theme-pack asset is missing
- 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/f01e7260a4d373dc.
Report an issue: GitHub.
Appendix: source
Thrown at HMCL/src/main/java/org/jackhuang/hmcl/theme/ThemePackManager.java:1241
try (ZipArchiveReader zipFile = new ZipArchiveReader(installedFile, StandardCharsets.UTF_8)) {
ZipArchiveEntry entry = zipFile.getEntry(normalizedEntryName);
if (entry == null || entry.isDirectory()) {
throw new IOException("Installed theme-pack asset is missing: " + normalizedEntryName);
}
}
return new ThemePackResource.Zip(installedFile, normalizedEntryName);
}
/// Resolves one asset stored in a bundled theme pack.
private static ThemePackResource resolveBuiltinAsset(ThemePackLocation.Builtin builtin, String entryName) throws IOException {
String id = ThemePackManifest.requirePackageId(builtin.id());
String resourcePath = "/assets/themes/" + id + "/" + entryName;
try (InputStream input = ThemePackManager.class.getResourceAsStream(resourcePath)) {
if (input != null) {
return new ThemePackResource.Builtin(resourcePath, entryName);
}
}
throw new IOException("Built-in theme-pack asset is missing: " + entryName);
}
/// Returns the installed theme-pack file under a theme-pack directory for one package ID.
private static Path installedThemePackFile(Path themePacksDirectory, String packId) {
String id = ThemePackManifest.requirePackageId(packId);
return themePacksDirectory
.resolve(id + ThemePackExporter.FILE_EXTENSION)
.toAbsolutePath()
.normalize();
}
/// Returns whether a path is an installed theme-pack file candidate.
private static boolean isInstalledThemePackFile(Path path) {
String fileName = path.getFileName().toString();
return !fileName.startsWith(".")
&& fileName.endsWith(ThemePackExporter.FILE_EXTENSION);
}
View on GitHub (pinned to 24702dc5a0)