halo-dev/halo · error · ThemeInstallationException
problemDetail.theme.install.missingManifest
problemDetail.theme.install.missingManifest
Error message
Missing theme manifest
What it means
ThemeInstallationException 'Missing theme manifest' (code problemDetail.theme.install.missingManifest) is thrown from two spots: (1) locateThemeManifest() found no theme.yaml/theme.yml anywhere in the extracted archive (the switchIfEmpty in unzipThemeTo); (2) loadThemeManifest() found a manifest file but YamlUnstructuredLoader loaded zero documents from it (empty or unparseable YAML). Either way Halo cannot identify the theme. THEME_MANIFESTS is {theme.yaml, theme.yml}.
Source
Thrown at application/src/main/java/run/halo/app/theme/service/ThemeUtils.java:151
copyRecursively(themeManifestPath.getParent(), themeTargetPath);
sink.next(theme);
} catch (IOException e) {
deleteRecursivelyAndSilently(themeTargetPath);
sink.error(e);
}
});
},
tempDir -> FileUtils.deleteRecursivelyAndSilently(tempDir, null));
if (scheduler != null) {
return unzipThem.subscribeOn(scheduler);
}
return unzipThem;
}
static Unstructured loadThemeManifest(Path themeManifestPath) {
var unstructureds = new YamlUnstructuredLoader(new FileSystemResource(themeManifestPath)).load();
if (CollectionUtils.isEmpty(unstructureds)) {
throw new ThemeInstallationException(
"Missing theme manifest", "problemDetail.theme.install.missingManifest", null);
}
return unstructureds.get(0);
}
@Nullable
static Path resolveThemeManifest(Path tempDirectory) {
for (String themeManifest : THEME_MANIFESTS) {
Path path = tempDirectory.resolve(themeManifest);
if (Files.exists(path)) {
return path;
}
}
return null;
}
static Optional<Path> locateThemeManifest(Path path) {
if (!Files.isDirectory(path)) {View on GitHub (pinned to d2f5165f9c)
Solutions
- Ensure the ZIP contains theme.yaml (or theme.yml) at the theme root; Halo searches recursively so put it inside the top-level theme folder.
- If the file exists, make sure it is valid, non-empty YAML with kind: Theme and metadata.name set.
- Re-zip so the manifest is included: zip -r theme.zip <theme-dir>/ where <theme-dir>/theme.yaml exists.
- Open theme.yaml locally and confirm it parses (e.g. yq or a YAML linter).
Example fix
# before: zipped the parent, theme.yaml missing from archive # zip -r out.zip . # theme.yaml not at expected place # after: zip the theme folder that holds theme.yaml # zip -r theme.zip my-theme/ # my-theme/theme.yaml exists
Defensive patterns
Strategy: validation
Validate before calling
// Verify a manifest exists and parses before installing:
Path manifest = resolveThemeManifest(themeRoot); // checks theme.yaml/theme.yml
if (manifest == null) {
throw new IllegalStateException("Missing theme.yaml/theme.yml in archive");
}
if (loadThemeManifest(manifest) == null) { // throws if empty/unparseable
throw new IllegalStateException("theme.yaml is empty or unparseable");
} Try / catch
try {
themeService.install(content).block();
} catch (ThemeInstallationException e) {
if ("problemDetail.theme.install.missingManifest".equals(e.getCode())) {
log.error("Theme archive has no valid theme.yaml/theme.yml");
}
throw e;
} Prevention
- Always include theme.yaml (or theme.yml) at the theme root inside the zip.
- Make sure theme.yaml is valid, non-empty YAML with kind: Theme and metadata.name.
- Zip the theme folder (which contains theme.yaml), not its parent.
- Lint theme.yaml (yq / YAML validator) before packaging.
When it happens
Trigger: Installing a ZIP that contains no theme.yaml/theme.yml at any depth; the manifest is present but empty; the manifest is malformed YAML that yields no Unstructured document; the file is named differently (e.g. theme.json or Theme.yaml with wrong case).
Common situations: Zipping the wrong folder (e.g. the parent instead of the theme root that has theme.yaml); case-sensitive filesystem mismatch; hand-authored theme missing the manifest; theme.yaml left empty by a broken generator.
Related errors
- Host runtime snapshot must expose exactly: ${SHARED_PACKAGE_
- ESM provider manifest must contain format, entry, and option
- Invalid attachment
- User permissions not set in PermissionUtils
- ESM provider manifest must be an object.
AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14).
Data as JSON: /api/errors/863caec06bf0c11d.
Report an issue: GitHub.