{"record":{"id":"863caec06bf0c11d","repo":"halo-dev/halo","slug":"problemdetail-theme-install-missingmanifest","errorCode":"problemDetail.theme.install.missingManifest","errorMessage":"Missing theme manifest","messagePattern":"Missing theme manifest","errorType":"exception","errorClass":"ThemeInstallationException","httpStatus":null,"severity":"error","filePath":"application/src/main/java/run/halo/app/theme/service/ThemeUtils.java","lineNumber":151,"sourceCode":"                                    copyRecursively(themeManifestPath.getParent(), themeTargetPath);\n                                    sink.next(theme);\n                                } catch (IOException e) {\n                                    deleteRecursivelyAndSilently(themeTargetPath);\n                                    sink.error(e);\n                                }\n                            });\n                },\n                tempDir -> FileUtils.deleteRecursivelyAndSilently(tempDir, null));\n        if (scheduler != null) {\n            return unzipThem.subscribeOn(scheduler);\n        }\n        return unzipThem;\n    }\n\n    static Unstructured loadThemeManifest(Path themeManifestPath) {\n        var unstructureds = new YamlUnstructuredLoader(new FileSystemResource(themeManifestPath)).load();\n        if (CollectionUtils.isEmpty(unstructureds)) {\n            throw new ThemeInstallationException(\n                    \"Missing theme manifest\", \"problemDetail.theme.install.missingManifest\", null);\n        }\n        return unstructureds.get(0);\n    }\n\n    @Nullable\n    static Path resolveThemeManifest(Path tempDirectory) {\n        for (String themeManifest : THEME_MANIFESTS) {\n            Path path = tempDirectory.resolve(themeManifest);\n            if (Files.exists(path)) {\n                return path;\n            }\n        }\n        return null;\n    }\n\n    static Optional<Path> locateThemeManifest(Path path) {\n        if (!Files.isDirectory(path)) {","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/halo-dev/halo/blob/d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8/application/src/main/java/run/halo/app/theme/service/ThemeUtils.java#L133-L169","documentation":"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}.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"# before: zipped the parent, theme.yaml missing from archive\n#   zip -r out.zip .          # theme.yaml not at expected place\n# after: zip the theme folder that holds theme.yaml\n#   zip -r theme.zip my-theme/   # my-theme/theme.yaml exists","handlingStrategy":"validation","validationCode":"// Verify a manifest exists and parses before installing:\nPath manifest = resolveThemeManifest(themeRoot); // checks theme.yaml/theme.yml\nif (manifest == null) {\n    throw new IllegalStateException(\"Missing theme.yaml/theme.yml in archive\");\n}\nif (loadThemeManifest(manifest) == null) { // throws if empty/unparseable\n    throw new IllegalStateException(\"theme.yaml is empty or unparseable\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    themeService.install(content).block();\n} catch (ThemeInstallationException e) {\n    if (\"problemDetail.theme.install.missingManifest\".equals(e.getCode())) {\n        log.error(\"Theme archive has no valid theme.yaml/theme.yml\");\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["theme","install","manifest","yaml","validation"],"backgroundTag":null,"analyzedSha":"d2f5165f9c8f055ffcb3fa9c3f4032821a7b68c8","analyzedAt":"2026-08-14T00:18:38.915Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}